From 74f2ece72b890b4c60cfb5f6fb4a332e6e5fa8f9 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 5 Feb 2025 17:19:11 +0000 Subject: [PATCH 01/11] Refactor automation builder. --- .../tests/triggers/createRow.spec.ts | 67 +++ .../automations/tests/triggers/cron.spec.ts | 2 +- .../tests/triggers/webhook.spec.ts | 2 - .../tests/utilities/AutomationTestBuilder.ts | 445 +++--------------- .../src/documents/app/automation/schema.ts | 23 + 5 files changed, 167 insertions(+), 372 deletions(-) create mode 100644 packages/server/src/automations/tests/triggers/createRow.spec.ts diff --git a/packages/server/src/automations/tests/triggers/createRow.spec.ts b/packages/server/src/automations/tests/triggers/createRow.spec.ts new file mode 100644 index 0000000000..84f136c8a6 --- /dev/null +++ b/packages/server/src/automations/tests/triggers/createRow.spec.ts @@ -0,0 +1,67 @@ +import { createAutomationBuilder } from "../utilities/AutomationTestBuilder" +import TestConfiguration from "../../../tests/utilities/TestConfiguration" +import { getQueue } from "../.." +import { Job } from "bull" +import { basicTable } from "../../../tests/utilities/structures" +import { Table } from "@budibase/types" + +describe("cron trigger", () => { + const config = new TestConfiguration() + let table: Table + + beforeAll(async () => { + await config.init() + table = await config.api.table.save(basicTable()) + }) + + afterAll(() => { + config.end() + }) + + it("should successfully fire", async () => { + const queue = getQueue() + expect(await queue.getCompletedCount()).toEqual(0) + + const jobPromise = new Promise(resolve => { + queue.on("completed", async job => { + resolve(job) + }) + }) + + await createAutomationBuilder({ config }) + .rowSaved({ tableId: table._id! }) + .cron({ cron: "* * * * *" }) + .serverLog({ + text: "Hello, world!", + }) + .save() + + await config.api.application.publish(config.getAppId()) + + expect(await queue.getCompletedCount()).toEqual(1) + + const job = await jobPromise + const repeat = job.opts?.repeat + if (!repeat || !("cron" in repeat)) { + throw new Error("Expected cron repeat") + } + expect(repeat.cron).toEqual("* * * * *") + }) + + it("should fail if the cron expression is invalid", async () => { + await createAutomationBuilder({ config }) + .cron({ cron: "* * * * * *" }) + .serverLog({ + text: "Hello, world!", + }) + .save() + + await config.api.application.publish(config.getAppId(), { + status: 500, + body: { + message: + 'Deployment Failed: Invalid automation CRON "* * * * * *" - Expected 5 values, but got 6.', + }, + }) + }) +}) diff --git a/packages/server/src/automations/tests/triggers/cron.spec.ts b/packages/server/src/automations/tests/triggers/cron.spec.ts index 956f054ca4..baddb1dd51 100644 --- a/packages/server/src/automations/tests/triggers/cron.spec.ts +++ b/packages/server/src/automations/tests/triggers/cron.spec.ts @@ -6,7 +6,7 @@ import { Job } from "bull" describe("cron trigger", () => { const config = new TestConfiguration() - beforeEach(async () => { + beforeAll(async () => { await config.init() }) diff --git a/packages/server/src/automations/tests/triggers/webhook.spec.ts b/packages/server/src/automations/tests/triggers/webhook.spec.ts index 2e0df5de49..a0b5e7f195 100644 --- a/packages/server/src/automations/tests/triggers/webhook.spec.ts +++ b/packages/server/src/automations/tests/triggers/webhook.spec.ts @@ -1,4 +1,3 @@ -import * as automation from "../../index" import { Table, Webhook, WebhookActionType } from "@budibase/types" import { createAutomationBuilder } from "../utilities/AutomationTestBuilder" import { mocks } from "@budibase/backend-core/tests" @@ -37,7 +36,6 @@ describe("Branching automations", () => { } beforeEach(async () => { - await automation.init() await config.init() table = await config.createTable() }) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 74e0ae87fb..9484f5cf21 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -2,51 +2,24 @@ import { v4 as uuidv4 } from "uuid" import { BUILTIN_ACTION_DEFINITIONS } from "../../actions" import { TRIGGER_DEFINITIONS } from "../../triggers" import { - AppActionTriggerInputs, AppActionTriggerOutputs, Automation, AutomationActionStepId, AutomationStep, AutomationStepInputs, AutomationTrigger, - AutomationTriggerDefinition, AutomationTriggerInputs, + AutomationTriggerOutputs, AutomationTriggerStepId, - BashStepInputs, - Branch, BranchStepInputs, - CollectStepInputs, - CreateRowStepInputs, - CronTriggerInputs, CronTriggerOutputs, - DelayStepInputs, - DeleteRowStepInputs, - DiscordStepInputs, - ExecuteQueryStepInputs, - ExecuteScriptStepInputs, - FilterStepInputs, isDidNotTriggerResponse, - LoopStepInputs, - MakeIntegrationInputs, - n8nStepInputs, - OpenAIStepInputs, - OutgoingWebhookStepInputs, - QueryRowsStepInputs, - RowCreatedTriggerInputs, RowCreatedTriggerOutputs, - RowDeletedTriggerInputs, RowDeletedTriggerOutputs, - RowUpdatedTriggerInputs, RowUpdatedTriggerOutputs, SearchFilters, - ServerLogStepInputs, - SmtpEmailStepInputs, TestAutomationRequest, - TriggerAutomationStepInputs, - UpdateRowStepInputs, - WebhookTriggerInputs, WebhookTriggerOutputs, - ZapierStepInputs, } from "@budibase/types" import TestConfiguration from "../../../tests/utilities/TestConfiguration" import * as setup from "../utilities" @@ -74,28 +47,53 @@ class BaseStepBuilder { protected steps: AutomationStep[] = [] protected stepNames: { [key: string]: string } = {} - protected step( - stepId: TStep, - stepSchema: Omit, - inputs: AutomationStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - const id = opts?.stepId || uuidv4() - this.steps.push({ - ...stepSchema, - inputs: inputs as any, - id, - stepId, - name: opts?.stepName || stepSchema.name, - }) - if (opts?.stepName) { - this.stepNames[id] = opts.stepName + protected createStepFn(stepId: TStep) { + return ( + inputs: AutomationStepInputs, + opts?: { stepName?: string; stepId?: string } + ) => { + const schema = BUILTIN_ACTION_DEFINITIONS[stepId] + const id = opts?.stepId || uuidv4() + this.steps.push({ + ...schema, + inputs: inputs as any, + id, + stepId, + name: opts?.stepName || schema.name, + }) + if (opts?.stepName) { + this.stepNames[id] = opts.stepName + } + return this } - return this } + + createRow = this.createStepFn(AutomationActionStepId.CREATE_ROW) + updateRow = this.createStepFn(AutomationActionStepId.UPDATE_ROW) + deleteRow = this.createStepFn(AutomationActionStepId.DELETE_ROW) + sendSmtpEmail = this.createStepFn(AutomationActionStepId.SEND_EMAIL_SMTP) + executeQuery = this.createStepFn(AutomationActionStepId.EXECUTE_QUERY) + queryRows = this.createStepFn(AutomationActionStepId.QUERY_ROWS) + loop = this.createStepFn(AutomationActionStepId.LOOP) + serverLog = this.createStepFn(AutomationActionStepId.SERVER_LOG) + executeScript = this.createStepFn(AutomationActionStepId.EXECUTE_SCRIPT) + filter = this.createStepFn(AutomationActionStepId.FILTER) + bash = this.createStepFn(AutomationActionStepId.EXECUTE_BASH) + openai = this.createStepFn(AutomationActionStepId.OPENAI) + collect = this.createStepFn(AutomationActionStepId.COLLECT) + zapier = this.createStepFn(AutomationActionStepId.zapier) + triggerAutomationRun = this.createStepFn( + AutomationActionStepId.TRIGGER_AUTOMATION_RUN + ) + outgoingWebhook = this.createStepFn(AutomationActionStepId.OUTGOING_WEBHOOK) + n8n = this.createStepFn(AutomationActionStepId.n8n) + make = this.createStepFn(AutomationActionStepId.integromat) + discord = this.createStepFn(AutomationActionStepId.discord) + delay = this.createStepFn(AutomationActionStepId.DELAY) + protected addBranchStep(branchConfig: BranchConfig): void { const branchStepInputs: BranchStepInputs = { - branches: [] as Branch[], + branches: [], children: {}, } @@ -118,243 +116,6 @@ class BaseStepBuilder { } this.steps.push(branchStep) } - - // STEPS - createRow( - inputs: CreateRowStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.CREATE_ROW, - BUILTIN_ACTION_DEFINITIONS.CREATE_ROW, - inputs, - opts - ) - } - - updateRow( - inputs: UpdateRowStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.UPDATE_ROW, - BUILTIN_ACTION_DEFINITIONS.UPDATE_ROW, - inputs, - opts - ) - } - - deleteRow( - inputs: DeleteRowStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.DELETE_ROW, - BUILTIN_ACTION_DEFINITIONS.DELETE_ROW, - inputs, - opts - ) - } - - sendSmtpEmail( - inputs: SmtpEmailStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.SEND_EMAIL_SMTP, - BUILTIN_ACTION_DEFINITIONS.SEND_EMAIL_SMTP, - inputs, - opts - ) - } - - executeQuery( - inputs: ExecuteQueryStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.EXECUTE_QUERY, - BUILTIN_ACTION_DEFINITIONS.EXECUTE_QUERY, - inputs, - opts - ) - } - - queryRows( - inputs: QueryRowsStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.QUERY_ROWS, - BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS, - inputs, - opts - ) - } - - loop( - inputs: LoopStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.LOOP, - BUILTIN_ACTION_DEFINITIONS.LOOP, - inputs, - opts - ) - } - - serverLog( - input: ServerLogStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.SERVER_LOG, - BUILTIN_ACTION_DEFINITIONS.SERVER_LOG, - input, - opts - ) - } - - executeScript( - input: ExecuteScriptStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.EXECUTE_SCRIPT, - BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT, - input, - opts - ) - } - - filter(input: FilterStepInputs): this { - return this.step( - AutomationActionStepId.FILTER, - BUILTIN_ACTION_DEFINITIONS.FILTER, - input - ) - } - - bash( - input: BashStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.EXECUTE_BASH, - BUILTIN_ACTION_DEFINITIONS.EXECUTE_BASH, - input, - opts - ) - } - - openai( - input: OpenAIStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.OPENAI, - BUILTIN_ACTION_DEFINITIONS.OPENAI, - input, - opts - ) - } - - collect( - input: CollectStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.COLLECT, - BUILTIN_ACTION_DEFINITIONS.COLLECT, - input, - opts - ) - } - - zapier( - input: ZapierStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.zapier, - BUILTIN_ACTION_DEFINITIONS.zapier, - input, - opts - ) - } - - triggerAutomationRun( - input: TriggerAutomationStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.TRIGGER_AUTOMATION_RUN, - BUILTIN_ACTION_DEFINITIONS.TRIGGER_AUTOMATION_RUN, - input, - opts - ) - } - - outgoingWebhook( - input: OutgoingWebhookStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.OUTGOING_WEBHOOK, - BUILTIN_ACTION_DEFINITIONS.OUTGOING_WEBHOOK, - input, - opts - ) - } - - n8n( - input: n8nStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.n8n, - BUILTIN_ACTION_DEFINITIONS.n8n, - input, - opts - ) - } - - make( - input: MakeIntegrationInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.integromat, - BUILTIN_ACTION_DEFINITIONS.integromat, - input, - opts - ) - } - - discord( - input: DiscordStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.discord, - BUILTIN_ACTION_DEFINITIONS.discord, - input, - opts - ) - } - - delay( - input: DelayStepInputs, - opts?: { stepName?: string; stepId?: string } - ): this { - return this.step( - AutomationActionStepId.DELAY, - BUILTIN_ACTION_DEFINITIONS.DELAY, - input, - opts - ) - } } class StepBuilder extends BaseStepBuilder { @@ -379,11 +140,17 @@ class AutomationBuilder extends BaseStepBuilder { ) { super() this.config = options.config || setup.getConfig() + this.triggerOutputs = { fields: {} } this.automationConfig = { name: options.name || `Test Automation ${uuidv4()}`, definition: { steps: [], - trigger: {} as AutomationTrigger, + trigger: { + ...TRIGGER_DEFINITIONS[AutomationTriggerStepId.APP], + stepId: AutomationTriggerStepId.APP, + inputs: this.triggerOutputs, + id: uuidv4(), + }, stepNames: {}, }, type: "automation", @@ -391,94 +158,34 @@ class AutomationBuilder extends BaseStepBuilder { } } - // TRIGGERS - rowSaved(inputs: RowCreatedTriggerInputs, outputs: RowCreatedTriggerOutputs) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.ROW_SAVED, - AutomationTriggerStepId.ROW_SAVED, - inputs, - outputs - ) - } - - rowUpdated( - inputs: RowUpdatedTriggerInputs, - outputs: RowUpdatedTriggerOutputs - ) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.ROW_UPDATED, - AutomationTriggerStepId.ROW_UPDATED, - inputs, - outputs - ) - } - - rowDeleted( - inputs: RowDeletedTriggerInputs, - outputs: RowDeletedTriggerOutputs - ) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.ROW_DELETED, - AutomationTriggerStepId.ROW_DELETED, - inputs, - outputs - ) - } - - appAction(outputs: AppActionTriggerOutputs, inputs?: AppActionTriggerInputs) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.APP, - AutomationTriggerStepId.APP, - inputs, - outputs - ) - } - - webhook(outputs: WebhookTriggerOutputs, inputs?: WebhookTriggerInputs) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.WEBHOOK, - AutomationTriggerStepId.WEBHOOK, - inputs, - outputs - ) - } - - cron(inputs: CronTriggerInputs, outputs?: CronTriggerOutputs) { - this.triggerOutputs = outputs - return this.trigger( - TRIGGER_DEFINITIONS.CRON, - AutomationTriggerStepId.CRON, - inputs, - outputs - ) - } - - private trigger( - triggerSchema: AutomationTriggerDefinition, - stepId: TStep, - inputs?: AutomationTriggerInputs, - outputs?: TriggerOutputs - ): this { - if (this.triggerSet) { - throw new Error("Only one trigger can be set for an automation.") + protected createTriggerFn< + TStep extends AutomationTriggerStepId, + TInput = AutomationTriggerInputs, + TOutput = AutomationTriggerOutputs + >(stepId: TStep) { + return (inputs: TInput, outputs?: TOutput) => { + if (this.triggerSet) { + throw new Error("Only one trigger can be set for an automation.") + } + this.triggerOutputs = outputs as TriggerOutputs | undefined + this.automationConfig.definition.trigger = { + ...TRIGGER_DEFINITIONS[stepId], + stepId, + inputs, + id: uuidv4(), + } as AutomationTrigger + this.triggerSet = true + return this } - this.automationConfig.definition.trigger = { - ...triggerSchema, - stepId, - inputs: inputs || ({} as any), - id: uuidv4(), - } - this.triggerOutputs = outputs - this.triggerSet = true - - return this } + rowSaved = this.createTriggerFn(AutomationTriggerStepId.ROW_SAVED) + rowUpdated = this.createTriggerFn(AutomationTriggerStepId.ROW_UPDATED) + rowDeleted = this.createTriggerFn(AutomationTriggerStepId.ROW_DELETED) + appAction = this.createTriggerFn(AutomationTriggerStepId.APP) + webhook = this.createTriggerFn(AutomationTriggerStepId.WEBHOOK) + cron = this.createTriggerFn(AutomationTriggerStepId.CRON) + branch(branchConfig: BranchConfig): this { this.addBranchStep(branchConfig) return this diff --git a/packages/types/src/documents/app/automation/schema.ts b/packages/types/src/documents/app/automation/schema.ts index 84bfebf6bf..952397b511 100644 --- a/packages/types/src/documents/app/automation/schema.ts +++ b/packages/types/src/documents/app/automation/schema.ts @@ -52,6 +52,12 @@ import { RowDeletedTriggerInputs, BranchStepInputs, BaseAutomationOutputs, + AppActionTriggerOutputs, + CronTriggerOutputs, + RowDeletedTriggerOutputs, + RowCreatedTriggerOutputs, + RowUpdatedTriggerOutputs, + WebhookTriggerOutputs, } from "./StepInputsOutputs" export type ActionImplementations = { @@ -341,6 +347,23 @@ export type AutomationTriggerInputs = ? Record : never +export type AutomationTriggerOutputs = + T extends AutomationTriggerStepId.APP + ? AppActionTriggerOutputs + : T extends AutomationTriggerStepId.CRON + ? CronTriggerOutputs + : T extends AutomationTriggerStepId.ROW_ACTION + ? Record + : T extends AutomationTriggerStepId.ROW_DELETED + ? RowDeletedTriggerOutputs + : T extends AutomationTriggerStepId.ROW_SAVED + ? RowCreatedTriggerOutputs + : T extends AutomationTriggerStepId.ROW_UPDATED + ? RowUpdatedTriggerOutputs + : T extends AutomationTriggerStepId.WEBHOOK + ? WebhookTriggerOutputs + : never + export interface AutomationTriggerSchema< TTrigger extends AutomationTriggerStepId > extends AutomationStepSchemaBase { From d2902464c06ff93f2f0ecb27736fdc9021d1a982 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 5 Feb 2025 17:30:48 +0000 Subject: [PATCH 02/11] Remove no longer needed appAction calls. --- .../src/api/routes/tests/automation.spec.ts | 1 - .../src/automations/tests/branching.spec.ts | 1 - .../src/automations/tests/scenarios.spec.ts | 8 --- .../src/automations/tests/steps/bash.spec.ts | 2 - .../src/automations/tests/steps/delay.spec.ts | 5 +- .../automations/tests/steps/deleteRow.spec.ts | 3 - .../automations/tests/steps/discord.spec.ts | 1 - .../tests/steps/executeScript.spec.ts | 3 - .../automations/tests/steps/filter.spec.ts | 2 - .../src/automations/tests/steps/loop.spec.ts | 9 --- .../src/automations/tests/steps/make.spec.ts | 3 - .../src/automations/tests/steps/n8n.spec.ts | 4 -- .../automations/tests/steps/openai.spec.ts | 4 -- .../tests/steps/outgoingWebhook.spec.ts | 2 - .../automations/tests/steps/queryRows.spec.ts | 6 -- .../automations/tests/steps/serverLog.spec.ts | 1 - .../tests/steps/triggerAutomationRun.spec.ts | 3 - .../automations/tests/steps/updateRow.spec.ts | 5 -- .../automations/tests/steps/zapier.spec.ts | 3 - .../tests/triggers/createRow.spec.ts | 67 ------------------- .../tests/utilities/AutomationTestBuilder.ts | 30 +++++++-- 21 files changed, 24 insertions(+), 139 deletions(-) delete mode 100644 packages/server/src/automations/tests/triggers/createRow.spec.ts diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index 94517db67a..eeceabdb2f 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -257,7 +257,6 @@ describe("/automations", () => { appId: config.getAppId(), config, }) - .appAction({ fields: {} }) .serverLog({ text: "{{ settings.url }}", }) diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index 1a514e0537..e6a250577d 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -25,7 +25,6 @@ describe("Branching automations", () => { const branch2Id = "44444444-4444-4444-4444-444444444444" const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .serverLog( { text: "Starting automation" }, { stepName: "FirstLog", stepId: firstLogId } diff --git a/packages/server/src/automations/tests/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios.spec.ts index 8d6e051ce2..6c40b793dd 100644 --- a/packages/server/src/automations/tests/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios.spec.ts @@ -66,7 +66,6 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows({ tableId: table._id!, }) @@ -85,7 +84,6 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows({ tableId: table._id!, }) @@ -127,7 +125,6 @@ describe("Automation Scenarios", () => { }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .createRow( { row: { @@ -196,7 +193,6 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -246,7 +242,6 @@ describe("Automation Scenarios", () => { it("should stop an automation if the condition is not met", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .createRow({ row: { name: "Equal Test", @@ -272,7 +267,6 @@ describe("Automation Scenarios", () => { it("should continue the automation if the condition is met", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .createRow({ row: { name: "Not Equal Test", @@ -339,7 +333,6 @@ describe("Automation Scenarios", () => { "should pass the filter when condition is $condition", async ({ condition, value, rowValue, expectPass }) => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .createRow({ row: { name: `${condition} Test`, @@ -389,7 +382,6 @@ describe("Automation Scenarios", () => { it("Check user is passed through from app trigger", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .serverLog({ text: "{{ [user].[email] }}" }) .run() diff --git a/packages/server/src/automations/tests/steps/bash.spec.ts b/packages/server/src/automations/tests/steps/bash.spec.ts index ef10f9b568..edf01bb3e2 100644 --- a/packages/server/src/automations/tests/steps/bash.spec.ts +++ b/packages/server/src/automations/tests/steps/bash.spec.ts @@ -65,7 +65,6 @@ describe("Execute Bash Automations", () => { it("should integrate bash output with row operations", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -122,7 +121,6 @@ describe("Execute Bash Automations", () => { it("should handle null values gracefully", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .bash( // @ts-expect-error - testing null input { code: null }, diff --git a/packages/server/src/automations/tests/steps/delay.spec.ts b/packages/server/src/automations/tests/steps/delay.spec.ts index a9e97b502f..89b85530e1 100644 --- a/packages/server/src/automations/tests/steps/delay.spec.ts +++ b/packages/server/src/automations/tests/steps/delay.spec.ts @@ -16,10 +16,7 @@ describe("test the delay logic", () => { const time = 100 const before = performance.now() - await createAutomationBuilder({ config }) - .appAction({ fields: {} }) - .delay({ time }) - .run() + await createAutomationBuilder({ config }).delay({ time }).run() const now = performance.now() diff --git a/packages/server/src/automations/tests/steps/deleteRow.spec.ts b/packages/server/src/automations/tests/steps/deleteRow.spec.ts index 1815edb4d3..52a5dacdd7 100644 --- a/packages/server/src/automations/tests/steps/deleteRow.spec.ts +++ b/packages/server/src/automations/tests/steps/deleteRow.spec.ts @@ -21,7 +21,6 @@ describe("test the delete row action", () => { it("should be able to run the delete row action", async () => { await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .deleteRow({ tableId: table._id!, id: row._id!, @@ -36,7 +35,6 @@ describe("test the delete row action", () => { it("should check invalid inputs return an error", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .deleteRow({ tableId: "", id: "", revision: "" }) .run() @@ -45,7 +43,6 @@ describe("test the delete row action", () => { it("should return an error when table doesn't exist", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .deleteRow({ tableId: "invalid", id: "invalid", diff --git a/packages/server/src/automations/tests/steps/discord.spec.ts b/packages/server/src/automations/tests/steps/discord.spec.ts index 5dc9c1088a..9d4d3f6033 100644 --- a/packages/server/src/automations/tests/steps/discord.spec.ts +++ b/packages/server/src/automations/tests/steps/discord.spec.ts @@ -20,7 +20,6 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .discord({ url: "http://www.example.com", username: "joe_bloggs", diff --git a/packages/server/src/automations/tests/steps/executeScript.spec.ts b/packages/server/src/automations/tests/steps/executeScript.spec.ts index 4470b8e760..2d510e86e0 100644 --- a/packages/server/src/automations/tests/steps/executeScript.spec.ts +++ b/packages/server/src/automations/tests/steps/executeScript.spec.ts @@ -21,7 +21,6 @@ describe("Execute Script Automations", () => { it("should execute a basic script and return the result", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .executeScript({ code: "return 2 + 2" }) .run() @@ -44,7 +43,6 @@ describe("Execute Script Automations", () => { it("should handle script execution errors gracefully", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .executeScript({ code: "return nonexistentVariable.map(x => x)" }) .run() @@ -73,7 +71,6 @@ describe("Execute Script Automations", () => { it("should use multiple steps and validate script execution", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .serverLog( { text: "Starting multi-step automation" }, { stepId: "start-log-step" } diff --git a/packages/server/src/automations/tests/steps/filter.spec.ts b/packages/server/src/automations/tests/steps/filter.spec.ts index b0943f3fa8..0d4ac0f01a 100644 --- a/packages/server/src/automations/tests/steps/filter.spec.ts +++ b/packages/server/src/automations/tests/steps/filter.spec.ts @@ -43,7 +43,6 @@ describe("test the filter logic", () => { ] it.each(pass)("should pass %p %p %p", async (field, condition, value) => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .filter({ field, condition: stringToFilterCondition(condition), value }) .run() @@ -61,7 +60,6 @@ describe("test the filter logic", () => { ] it.each(fail)("should fail %p %p %p", async (field, condition, value) => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .filter({ field, condition: stringToFilterCondition(condition), value }) .run() diff --git a/packages/server/src/automations/tests/steps/loop.spec.ts b/packages/server/src/automations/tests/steps/loop.spec.ts index af174c33ca..764f55d9ad 100644 --- a/packages/server/src/automations/tests/steps/loop.spec.ts +++ b/packages/server/src/automations/tests/steps/loop.spec.ts @@ -152,7 +152,6 @@ describe("Attempt to run a basic loop automation", () => { it("if an incorrect type is passed to the loop it should return an error", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop({ option: LoopStepType.ARRAY, binding: "1, 2, 3", @@ -168,7 +167,6 @@ describe("Attempt to run a basic loop automation", () => { it("ensure the loop stops if the failure condition is reached", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -187,7 +185,6 @@ describe("Attempt to run a basic loop automation", () => { it("ensure the loop stops if the max iterations are reached", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -202,7 +199,6 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation with loop and max iterations to ensure context correctness further down the tree", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -279,7 +275,6 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation where a loop is used twice to ensure context correctness further down the tree", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop({ option: LoopStepType.ARRAY, binding: [1, 2, 3], @@ -301,7 +296,6 @@ describe("Attempt to run a basic loop automation", () => { it("should use automation names to loop with", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .loop( { option: LoopStepType.ARRAY, @@ -353,7 +347,6 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows({ tableId: table._id!, }) @@ -433,7 +426,6 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -516,7 +508,6 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .queryRows({ tableId: table._id!, }) diff --git a/packages/server/src/automations/tests/steps/make.spec.ts b/packages/server/src/automations/tests/steps/make.spec.ts index 8ba5d3f8b7..a26f9f73ae 100644 --- a/packages/server/src/automations/tests/steps/make.spec.ts +++ b/packages/server/src/automations/tests/steps/make.spec.ts @@ -20,7 +20,6 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .make({ url: "http://www.example.com", body: null, @@ -47,7 +46,6 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .make({ body: { value: JSON.stringify(payload) }, url: "http://www.example.com", @@ -60,7 +58,6 @@ describe("test the outgoing webhook action", () => { it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .make({ body: { value: "{ invalid json }" }, url: "http://www.example.com", diff --git a/packages/server/src/automations/tests/steps/n8n.spec.ts b/packages/server/src/automations/tests/steps/n8n.spec.ts index 4427ea33e0..04d82fcd77 100644 --- a/packages/server/src/automations/tests/steps/n8n.spec.ts +++ b/packages/server/src/automations/tests/steps/n8n.spec.ts @@ -21,7 +21,6 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action and default to 'get'", async () => { nock("http://www.example.com/").get("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .n8n({ url: "http://www.example.com", body: { test: "IGNORE_ME" }, @@ -40,7 +39,6 @@ describe("test the outgoing webhook action", () => { .reply(200) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .n8n({ url: "http://www.example.com", body: { value: JSON.stringify({ name: "Adam", age: 9 }) }, @@ -54,7 +52,6 @@ describe("test the outgoing webhook action", () => { it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .n8n({ url: "http://www.example.com", body: { value: "{ value1 1 }" }, @@ -74,7 +71,6 @@ describe("test the outgoing webhook action", () => { .reply(200) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .n8n({ url: "http://www.example.com", method: HttpMethod.HEAD, diff --git a/packages/server/src/automations/tests/steps/openai.spec.ts b/packages/server/src/automations/tests/steps/openai.spec.ts index 3030b3db39..7e9fa1f933 100644 --- a/packages/server/src/automations/tests/steps/openai.spec.ts +++ b/packages/server/src/automations/tests/steps/openai.spec.ts @@ -58,7 +58,6 @@ describe("test the openai action", () => { // own API key. We don't count this against your quota. const result = await expectAIUsage(0, () => createAutomationBuilder({ config }) - .appAction({ fields: {} }) .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run() ) @@ -70,7 +69,6 @@ describe("test the openai action", () => { it("should present the correct error message when a prompt is not provided", async () => { const result = await expectAIUsage(0, () => createAutomationBuilder({ config }) - .appAction({ fields: {} }) .openai({ prompt: "", model: Model.GPT_4O_MINI }) .run() ) @@ -86,7 +84,6 @@ describe("test the openai action", () => { const result = await expectAIUsage(0, () => createAutomationBuilder({ config }) - .appAction({ fields: {} }) .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run() ) @@ -109,7 +106,6 @@ describe("test the openai action", () => { // key, so we charge users for it. const result = await expectAIUsage(14, () => createAutomationBuilder({ config }) - .appAction({ fields: {} }) .openai({ model: Model.GPT_4O_MINI, prompt: "Hello, world" }) .run() ) diff --git a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts index b1ff5aa264..9ef3cf1e57 100644 --- a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts +++ b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts @@ -24,7 +24,6 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .outgoingWebhook({ requestMethod: RequestType.POST, url: "http://www.example.com", @@ -40,7 +39,6 @@ describe("test the outgoing webhook action", () => { it("should return an error if something goes wrong in fetch", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .outgoingWebhook({ requestMethod: RequestType.GET, url: "www.invalid.com", diff --git a/packages/server/src/automations/tests/steps/queryRows.spec.ts b/packages/server/src/automations/tests/steps/queryRows.spec.ts index 0030e7fc61..1d79d8e046 100644 --- a/packages/server/src/automations/tests/steps/queryRows.spec.ts +++ b/packages/server/src/automations/tests/steps/queryRows.spec.ts @@ -32,7 +32,6 @@ describe("Test a query step automation", () => { name: "Basic Query Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -60,7 +59,6 @@ describe("Test a query step automation", () => { name: "Empty Filter Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -84,7 +82,6 @@ describe("Test a query step automation", () => { name: "Return None Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -109,7 +106,6 @@ describe("Test a query step automation", () => { name: "Null Filter Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -138,7 +134,6 @@ describe("Test a query step automation", () => { name: "Return All Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: table._id!, @@ -169,7 +164,6 @@ describe("Test a query step automation", () => { name: "Return All Test", config, }) - .appAction({ fields: {} }) .queryRows( { tableId: tableWithSpaces._id!, diff --git a/packages/server/src/automations/tests/steps/serverLog.spec.ts b/packages/server/src/automations/tests/steps/serverLog.spec.ts index 556f7b174c..73d72b9a99 100644 --- a/packages/server/src/automations/tests/steps/serverLog.spec.ts +++ b/packages/server/src/automations/tests/steps/serverLog.spec.ts @@ -14,7 +14,6 @@ describe("test the server log action", () => { it("should be able to log the text", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .serverLog({ text: "Hello World" }) .run() expect(result.steps[0].outputs.message).toEqual( diff --git a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts index 674f2386c1..4047c23566 100644 --- a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts +++ b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts @@ -18,12 +18,10 @@ describe("Test triggering an automation from another automation", () => { it("should trigger an other server log automation", async () => { const automation = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .serverLog({ text: "Hello World" }) .save() const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .triggerAutomationRun({ automation: { automationId: automation._id!, @@ -37,7 +35,6 @@ describe("Test triggering an automation from another automation", () => { it("should fail gracefully if the automation id is incorrect", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .triggerAutomationRun({ automation: { // @ts-expect-error - incorrect on purpose diff --git a/packages/server/src/automations/tests/steps/updateRow.spec.ts b/packages/server/src/automations/tests/steps/updateRow.spec.ts index 79fed5f613..ca3854f745 100644 --- a/packages/server/src/automations/tests/steps/updateRow.spec.ts +++ b/packages/server/src/automations/tests/steps/updateRow.spec.ts @@ -31,7 +31,6 @@ describe("test the update row action", () => { it("should be able to run the update row action", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .updateRow({ rowId: row._id!, row: { @@ -54,7 +53,6 @@ describe("test the update row action", () => { it("should check invalid inputs return an error", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .updateRow({ meta: {}, row: {}, rowId: "" }) .run() @@ -63,7 +61,6 @@ describe("test the update row action", () => { it("should return an error when table doesn't exist", async () => { const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .updateRow({ row: { _id: "invalid" }, rowId: "invalid", @@ -107,7 +104,6 @@ describe("test the update row action", () => { }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .updateRow({ rowId: row._id!, row: { @@ -161,7 +157,6 @@ describe("test the update row action", () => { }) const results = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .updateRow({ rowId: row._id!, row: { diff --git a/packages/server/src/automations/tests/steps/zapier.spec.ts b/packages/server/src/automations/tests/steps/zapier.spec.ts index 17a0a7c7bf..331acb68f3 100644 --- a/packages/server/src/automations/tests/steps/zapier.spec.ts +++ b/packages/server/src/automations/tests/steps/zapier.spec.ts @@ -21,7 +21,6 @@ describe("test the outgoing webhook action", () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .zapier({ url: "http://www.example.com", body: null }) .run() @@ -45,7 +44,6 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .zapier({ url: "http://www.example.com", body: { value: JSON.stringify(payload) }, @@ -58,7 +56,6 @@ describe("test the outgoing webhook action", () => { it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder({ config }) - .appAction({ fields: {} }) .zapier({ url: "http://www.example.com", body: { value: "{ invalid json }" }, diff --git a/packages/server/src/automations/tests/triggers/createRow.spec.ts b/packages/server/src/automations/tests/triggers/createRow.spec.ts deleted file mode 100644 index 84f136c8a6..0000000000 --- a/packages/server/src/automations/tests/triggers/createRow.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { createAutomationBuilder } from "../utilities/AutomationTestBuilder" -import TestConfiguration from "../../../tests/utilities/TestConfiguration" -import { getQueue } from "../.." -import { Job } from "bull" -import { basicTable } from "../../../tests/utilities/structures" -import { Table } from "@budibase/types" - -describe("cron trigger", () => { - const config = new TestConfiguration() - let table: Table - - beforeAll(async () => { - await config.init() - table = await config.api.table.save(basicTable()) - }) - - afterAll(() => { - config.end() - }) - - it("should successfully fire", async () => { - const queue = getQueue() - expect(await queue.getCompletedCount()).toEqual(0) - - const jobPromise = new Promise(resolve => { - queue.on("completed", async job => { - resolve(job) - }) - }) - - await createAutomationBuilder({ config }) - .rowSaved({ tableId: table._id! }) - .cron({ cron: "* * * * *" }) - .serverLog({ - text: "Hello, world!", - }) - .save() - - await config.api.application.publish(config.getAppId()) - - expect(await queue.getCompletedCount()).toEqual(1) - - const job = await jobPromise - const repeat = job.opts?.repeat - if (!repeat || !("cron" in repeat)) { - throw new Error("Expected cron repeat") - } - expect(repeat.cron).toEqual("* * * * *") - }) - - it("should fail if the cron expression is invalid", async () => { - await createAutomationBuilder({ config }) - .cron({ cron: "* * * * * *" }) - .serverLog({ - text: "Hello, world!", - }) - .save() - - await config.api.application.publish(config.getAppId(), { - status: 500, - body: { - message: - 'Deployment Failed: Invalid automation CRON "* * * * * *" - Expected 5 values, but got 6.', - }, - }) - }) -}) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 9484f5cf21..e059bd1c04 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -158,7 +158,7 @@ class AutomationBuilder extends BaseStepBuilder { } } - protected createTriggerFn< + protected triggerInputOutput< TStep extends AutomationTriggerStepId, TInput = AutomationTriggerInputs, TOutput = AutomationTriggerOutputs @@ -179,12 +179,28 @@ class AutomationBuilder extends BaseStepBuilder { } } - rowSaved = this.createTriggerFn(AutomationTriggerStepId.ROW_SAVED) - rowUpdated = this.createTriggerFn(AutomationTriggerStepId.ROW_UPDATED) - rowDeleted = this.createTriggerFn(AutomationTriggerStepId.ROW_DELETED) - appAction = this.createTriggerFn(AutomationTriggerStepId.APP) - webhook = this.createTriggerFn(AutomationTriggerStepId.WEBHOOK) - cron = this.createTriggerFn(AutomationTriggerStepId.CRON) + protected triggerOutputOnly< + TStep extends AutomationTriggerStepId, + TOutput = AutomationTriggerOutputs + >(stepId: TStep) { + return (outputs: TOutput) => { + this.triggerOutputs = outputs as TriggerOutputs + this.automationConfig.definition.trigger = { + ...TRIGGER_DEFINITIONS[stepId], + stepId, + id: uuidv4(), + } as AutomationTrigger + this.triggerSet = true + return this + } + } + + rowSaved = this.triggerInputOutput(AutomationTriggerStepId.ROW_SAVED) + rowUpdated = this.triggerInputOutput(AutomationTriggerStepId.ROW_UPDATED) + rowDeleted = this.triggerInputOutput(AutomationTriggerStepId.ROW_DELETED) + appAction = this.triggerOutputOnly(AutomationTriggerStepId.APP) + webhook = this.triggerInputOutput(AutomationTriggerStepId.WEBHOOK) + cron = this.triggerInputOutput(AutomationTriggerStepId.CRON) branch(branchConfig: BranchConfig): this { this.addBranchStep(branchConfig) From d62be2629c43a1b5480dc4ee4191384924d85469 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 5 Feb 2025 17:39:38 +0000 Subject: [PATCH 03/11] Simplify calls to createAutomationBuilder. --- .../src/api/routes/tests/automation.spec.ts | 26 +++------------- .../src/automations/tests/branching.spec.ts | 14 ++++----- .../src/automations/tests/scenarios.spec.ts | 22 ++++++------- .../src/automations/tests/steps/bash.spec.ts | 10 +++--- .../automations/tests/steps/createRow.spec.ts | 12 +++---- .../tests/steps/cron-automations.spec.ts | 2 +- .../src/automations/tests/steps/delay.spec.ts | 2 +- .../automations/tests/steps/deleteRow.spec.ts | 6 ++-- .../automations/tests/steps/discord.spec.ts | 2 +- .../tests/steps/executeScript.spec.ts | 11 ++++--- .../automations/tests/steps/filter.spec.ts | 4 +-- .../src/automations/tests/steps/loop.spec.ts | 24 +++++++------- .../src/automations/tests/steps/make.spec.ts | 6 ++-- .../src/automations/tests/steps/n8n.spec.ts | 8 ++--- .../automations/tests/steps/openai.spec.ts | 8 ++--- .../tests/steps/outgoingWebhook.spec.ts | 4 +-- .../automations/tests/steps/queryRows.spec.ts | 30 ++++-------------- .../automations/tests/steps/serverLog.spec.ts | 2 +- .../tests/steps/triggerAutomationRun.spec.ts | 6 ++-- .../automations/tests/steps/updateRow.spec.ts | 10 +++--- .../automations/tests/steps/zapier.spec.ts | 6 ++-- .../automations/tests/triggers/cron.spec.ts | 4 +-- .../tests/triggers/webhook.spec.ts | 2 +- .../tests/utilities/AutomationTestBuilder.ts | 31 +++++++++---------- 24 files changed, 109 insertions(+), 143 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index eeceabdb2f..5b451eef4a 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -107,10 +107,7 @@ describe("/automations", () => { }) it("Should ensure you can't have a branch as not a last step", async () => { - const automation = createAutomationBuilder({ - name: "String Equality Branching", - appId: config.getAppId(), - }) + const automation = createAutomationBuilder(config) .appAction({ fields: { status: "active" } }) .branch({ activeBranch: { @@ -134,10 +131,7 @@ describe("/automations", () => { }) it("Should check validation on an automation that has a branch step with no children", async () => { - const automation = createAutomationBuilder({ - name: "String Equality Branching", - appId: config.getAppId(), - }) + const automation = createAutomationBuilder(config) .appAction({ fields: { status: "active" } }) .branch({}) .serverLog({ text: "Inactive user" }) @@ -153,10 +147,7 @@ describe("/automations", () => { }) it("Should check validation on a branch step with empty conditions", async () => { - const automation = createAutomationBuilder({ - name: "String Equality Branching", - appId: config.getAppId(), - }) + const automation = createAutomationBuilder(config) .appAction({ fields: { status: "active" } }) .branch({ activeBranch: { @@ -177,10 +168,7 @@ describe("/automations", () => { }) it("Should check validation on an branch that has a condition that is not valid", async () => { - const automation = createAutomationBuilder({ - name: "String Equality Branching", - appId: config.getAppId(), - }) + const automation = createAutomationBuilder(config) .appAction({ fields: { status: "active" } }) .branch({ activeBranch: { @@ -252,11 +240,7 @@ describe("/automations", () => { }) it("should be able to access platformUrl, logoUrl and company in the automation", async () => { - const result = await createAutomationBuilder({ - name: "Test Automation", - appId: config.getAppId(), - config, - }) + const result = await createAutomationBuilder(config) .serverLog({ text: "{{ settings.url }}", }) diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index e6a250577d..d9fad543f6 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -24,7 +24,7 @@ describe("Branching automations", () => { const branch2LogId = "33333333-3333-3333-3333-333333333333" const branch2Id = "44444444-4444-4444-4444-444444444444" - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .serverLog( { text: "Starting automation" }, { stepName: "FirstLog", stepId: firstLogId } @@ -82,7 +82,7 @@ describe("Branching automations", () => { }) it("should execute correct branch based on string equality", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { status: "active" } }) .branch({ activeBranch: { @@ -107,7 +107,7 @@ describe("Branching automations", () => { }) it("should handle multiple conditions with AND operator", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { status: "active", role: "admin" } }) .branch({ activeAdminBranch: { @@ -135,7 +135,7 @@ describe("Branching automations", () => { }) it("should handle multiple conditions with OR operator", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { status: "test", role: "user" } }) .branch({ specialBranch: { @@ -167,7 +167,7 @@ describe("Branching automations", () => { }) it("should stop the branch automation when no conditions are met", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { status: "test", role: "user" } }) .createRow({ row: { name: "Test", tableId: table._id } }) .branch({ @@ -203,7 +203,7 @@ describe("Branching automations", () => { }) it("evaluate multiple conditions", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { test_trigger: true } }) .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ @@ -244,7 +244,7 @@ describe("Branching automations", () => { }) it("evaluate multiple conditions with interpolated text", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { test_trigger: true } }) .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ diff --git a/packages/server/src/automations/tests/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios.spec.ts index 6c40b793dd..2c13b7c019 100644 --- a/packages/server/src/automations/tests/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios.spec.ts @@ -29,7 +29,7 @@ describe("Automation Scenarios", () => { it("should trigger an automation which then creates a row", async () => { const table = await config.api.table.save(basicTable()) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .rowUpdated( { tableId: table._id! }, { @@ -65,7 +65,7 @@ describe("Automation Scenarios", () => { } await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows({ tableId: table._id!, }) @@ -83,7 +83,7 @@ describe("Automation Scenarios", () => { } await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows({ tableId: table._id!, }) @@ -124,7 +124,7 @@ describe("Automation Scenarios", () => { }, }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .createRow( { row: { @@ -192,7 +192,7 @@ describe("Automation Scenarios", () => { } await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -241,7 +241,7 @@ describe("Automation Scenarios", () => { }) it("should stop an automation if the condition is not met", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .createRow({ row: { name: "Equal Test", @@ -266,7 +266,7 @@ describe("Automation Scenarios", () => { }) it("should continue the automation if the condition is met", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .createRow({ row: { name: "Not Equal Test", @@ -332,7 +332,7 @@ describe("Automation Scenarios", () => { it.each(testCases)( "should pass the filter when condition is $condition", async ({ condition, value, rowValue, expectPass }) => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .createRow({ row: { name: `${condition} Test`, @@ -366,7 +366,7 @@ describe("Automation Scenarios", () => { it("Check user is passed through from row trigger", async () => { const table = await config.api.table.save(basicTable()) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .rowUpdated( { tableId: table._id! }, { @@ -381,7 +381,7 @@ describe("Automation Scenarios", () => { }) it("Check user is passed through from app trigger", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .serverLog({ text: "{{ [user].[email] }}" }) .run() @@ -452,7 +452,7 @@ if (descriptions.length) { queryVerb: "read", }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: {}, }) diff --git a/packages/server/src/automations/tests/steps/bash.spec.ts b/packages/server/src/automations/tests/steps/bash.spec.ts index edf01bb3e2..9b797f9479 100644 --- a/packages/server/src/automations/tests/steps/bash.spec.ts +++ b/packages/server/src/automations/tests/steps/bash.spec.ts @@ -24,7 +24,7 @@ describe("Execute Bash Automations", () => { }) it("should use trigger data in bash command and pass output to subsequent steps", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { command: "hello world" } }) .bash( { code: "echo '{{ trigger.fields.command }}'" }, @@ -43,7 +43,7 @@ describe("Execute Bash Automations", () => { }) it("should chain multiple bash commands using previous outputs", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { filename: "testfile.txt" } }) .bash( { code: "echo 'initial content' > {{ trigger.fields.filename }}" }, @@ -64,7 +64,7 @@ describe("Execute Bash Automations", () => { }) it("should integrate bash output with row operations", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -93,7 +93,7 @@ describe("Execute Bash Automations", () => { }) it("should handle bash output in conditional logic", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { threshold: "5" } }) .bash( { code: "echo $(( {{ trigger.fields.threshold }} + 5 ))" }, @@ -120,7 +120,7 @@ describe("Execute Bash Automations", () => { }) it("should handle null values gracefully", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .bash( // @ts-expect-error - testing null input { code: null }, diff --git a/packages/server/src/automations/tests/steps/createRow.spec.ts b/packages/server/src/automations/tests/steps/createRow.spec.ts index 5b86556ae3..4456550cb3 100644 --- a/packages/server/src/automations/tests/steps/createRow.spec.ts +++ b/packages/server/src/automations/tests/steps/createRow.spec.ts @@ -40,7 +40,7 @@ describe("test the create row action", () => { }) it("should be able to run the action", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { status: "new" } }) .serverLog({ text: "Starting create row flow" }, { stepName: "StartLog" }) .createRow({ row }, { stepName: "CreateRow" }) @@ -66,7 +66,7 @@ describe("test the create row action", () => { }) it("should return an error (not throw) when bad info provided", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { status: "error" } }) .serverLog({ text: "Starting error test flow" }, { stepName: "StartLog" }) .createRow( @@ -84,7 +84,7 @@ describe("test the create row action", () => { }) it("should check invalid inputs return an error", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { status: "invalid" } }) .serverLog({ text: "Testing invalid input" }, { stepName: "StartLog" }) .createRow({ row: {} }, { stepName: "CreateRow" }) @@ -122,7 +122,7 @@ describe("test the create row action", () => { ] attachmentRow.file_attachment = attachmentObject - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { type: "attachment" } }) .serverLog( { text: "Processing attachment upload" }, @@ -173,7 +173,7 @@ describe("test the create row action", () => { } attachmentRow.single_file_attachment = attachmentObject - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { type: "single-attachment" } }) .serverLog( { text: "Processing single attachment" }, @@ -244,7 +244,7 @@ describe("test the create row action", () => { } attachmentRow.single_file_attachment = attachmentObject - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .appAction({ fields: { type: "invalid-attachment" } }) .serverLog( { text: "Testing invalid attachment keys" }, diff --git a/packages/server/src/automations/tests/steps/cron-automations.spec.ts b/packages/server/src/automations/tests/steps/cron-automations.spec.ts index 6a82ac1cde..ed0729bd38 100644 --- a/packages/server/src/automations/tests/steps/cron-automations.spec.ts +++ b/packages/server/src/automations/tests/steps/cron-automations.spec.ts @@ -27,7 +27,7 @@ describe("cron automations", () => { }) it("should initialise the automation timestamp", async () => { - await createAutomationBuilder({ config }).cron({ cron: "* * * * *" }).save() + await createAutomationBuilder(config).cron({ cron: "* * * * *" }).save() tk.travel(Date.now() + oneMinuteInMs) await config.publish() diff --git a/packages/server/src/automations/tests/steps/delay.spec.ts b/packages/server/src/automations/tests/steps/delay.spec.ts index 89b85530e1..9dc6470f5a 100644 --- a/packages/server/src/automations/tests/steps/delay.spec.ts +++ b/packages/server/src/automations/tests/steps/delay.spec.ts @@ -16,7 +16,7 @@ describe("test the delay logic", () => { const time = 100 const before = performance.now() - await createAutomationBuilder({ config }).delay({ time }).run() + await createAutomationBuilder(config).delay({ time }).run() const now = performance.now() diff --git a/packages/server/src/automations/tests/steps/deleteRow.spec.ts b/packages/server/src/automations/tests/steps/deleteRow.spec.ts index 52a5dacdd7..ee8c9b329f 100644 --- a/packages/server/src/automations/tests/steps/deleteRow.spec.ts +++ b/packages/server/src/automations/tests/steps/deleteRow.spec.ts @@ -20,7 +20,7 @@ describe("test the delete row action", () => { }) it("should be able to run the delete row action", async () => { - await createAutomationBuilder({ config }) + await createAutomationBuilder(config) .deleteRow({ tableId: table._id!, id: row._id!, @@ -34,7 +34,7 @@ describe("test the delete row action", () => { }) it("should check invalid inputs return an error", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .deleteRow({ tableId: "", id: "", revision: "" }) .run() @@ -42,7 +42,7 @@ describe("test the delete row action", () => { }) it("should return an error when table doesn't exist", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .deleteRow({ tableId: "invalid", id: "invalid", diff --git a/packages/server/src/automations/tests/steps/discord.spec.ts b/packages/server/src/automations/tests/steps/discord.spec.ts index 9d4d3f6033..361b3517f3 100644 --- a/packages/server/src/automations/tests/steps/discord.spec.ts +++ b/packages/server/src/automations/tests/steps/discord.spec.ts @@ -19,7 +19,7 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .discord({ url: "http://www.example.com", username: "joe_bloggs", diff --git a/packages/server/src/automations/tests/steps/executeScript.spec.ts b/packages/server/src/automations/tests/steps/executeScript.spec.ts index 2d510e86e0..e2bea9d0c1 100644 --- a/packages/server/src/automations/tests/steps/executeScript.spec.ts +++ b/packages/server/src/automations/tests/steps/executeScript.spec.ts @@ -20,7 +20,7 @@ describe("Execute Script Automations", () => { }) it("should execute a basic script and return the result", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .executeScript({ code: "return 2 + 2" }) .run() @@ -28,7 +28,7 @@ describe("Execute Script Automations", () => { }) it("should access bindings from previous steps", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { data: [1, 2, 3] } }) .executeScript( { @@ -42,7 +42,7 @@ describe("Execute Script Automations", () => { }) it("should handle script execution errors gracefully", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .executeScript({ code: "return nonexistentVariable.map(x => x)" }) .run() @@ -53,7 +53,7 @@ describe("Execute Script Automations", () => { }) it("should handle conditional logic in scripts", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .appAction({ fields: { value: 10 } }) .executeScript({ code: ` @@ -70,7 +70,8 @@ describe("Execute Script Automations", () => { }) it("should use multiple steps and validate script execution", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) + .appAction({ fields: {} }) .serverLog( { text: "Starting multi-step automation" }, { stepId: "start-log-step" } diff --git a/packages/server/src/automations/tests/steps/filter.spec.ts b/packages/server/src/automations/tests/steps/filter.spec.ts index 0d4ac0f01a..51af262aff 100644 --- a/packages/server/src/automations/tests/steps/filter.spec.ts +++ b/packages/server/src/automations/tests/steps/filter.spec.ts @@ -42,7 +42,7 @@ describe("test the filter logic", () => { [new Date().toISOString(), ">", new Date(-10000).toISOString()], ] it.each(pass)("should pass %p %p %p", async (field, condition, value) => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .filter({ field, condition: stringToFilterCondition(condition), value }) .run() @@ -59,7 +59,7 @@ describe("test the filter logic", () => { [{}, "==", {}], ] it.each(fail)("should fail %p %p %p", async (field, condition, value) => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .filter({ field, condition: stringToFilterCondition(condition), value }) .run() diff --git a/packages/server/src/automations/tests/steps/loop.spec.ts b/packages/server/src/automations/tests/steps/loop.spec.ts index 764f55d9ad..ba74bf6778 100644 --- a/packages/server/src/automations/tests/steps/loop.spec.ts +++ b/packages/server/src/automations/tests/steps/loop.spec.ts @@ -72,7 +72,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should run an automation with a trigger, loop, and create row step", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .rowSaved( { tableId: table._id! }, { @@ -115,7 +115,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should run an automation where a loop step is between two normal steps to ensure context correctness", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .rowSaved( { tableId: table._id! }, { @@ -151,7 +151,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("if an incorrect type is passed to the loop it should return an error", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop({ option: LoopStepType.ARRAY, binding: "1, 2, 3", @@ -166,7 +166,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("ensure the loop stops if the failure condition is reached", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -184,7 +184,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("ensure the loop stops if the max iterations are reached", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -198,7 +198,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should run an automation with loop and max iterations to ensure context correctness further down the tree", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -212,7 +212,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should run an automation where a loop is successfully run twice", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .rowSaved( { tableId: table._id! }, { @@ -274,7 +274,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should run an automation where a loop is used twice to ensure context correctness further down the tree", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop({ option: LoopStepType.ARRAY, binding: [1, 2, 3], @@ -295,7 +295,7 @@ describe("Attempt to run a basic loop automation", () => { }) it("should use automation names to loop with", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .loop( { option: LoopStepType.ARRAY, @@ -346,7 +346,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows({ tableId: table._id!, }) @@ -425,7 +425,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -507,7 +507,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .queryRows({ tableId: table._id!, }) diff --git a/packages/server/src/automations/tests/steps/make.spec.ts b/packages/server/src/automations/tests/steps/make.spec.ts index a26f9f73ae..2d118d943f 100644 --- a/packages/server/src/automations/tests/steps/make.spec.ts +++ b/packages/server/src/automations/tests/steps/make.spec.ts @@ -19,7 +19,7 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .make({ url: "http://www.example.com", body: null, @@ -45,7 +45,7 @@ describe("test the outgoing webhook action", () => { .post("/", payload) .reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .make({ body: { value: JSON.stringify(payload) }, url: "http://www.example.com", @@ -57,7 +57,7 @@ describe("test the outgoing webhook action", () => { }) it("should return a 400 if the JSON payload string is malformed", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .make({ body: { value: "{ invalid json }" }, url: "http://www.example.com", diff --git a/packages/server/src/automations/tests/steps/n8n.spec.ts b/packages/server/src/automations/tests/steps/n8n.spec.ts index 04d82fcd77..d3efb1aaeb 100644 --- a/packages/server/src/automations/tests/steps/n8n.spec.ts +++ b/packages/server/src/automations/tests/steps/n8n.spec.ts @@ -20,7 +20,7 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action and default to 'get'", async () => { nock("http://www.example.com/").get("/").reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .n8n({ url: "http://www.example.com", body: { test: "IGNORE_ME" }, @@ -38,7 +38,7 @@ describe("test the outgoing webhook action", () => { .post("/", { name: "Adam", age: 9 }) .reply(200) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .n8n({ url: "http://www.example.com", body: { value: JSON.stringify({ name: "Adam", age: 9 }) }, @@ -51,7 +51,7 @@ describe("test the outgoing webhook action", () => { }) it("should return a 400 if the JSON payload string is malformed", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .n8n({ url: "http://www.example.com", body: { value: "{ value1 1 }" }, @@ -70,7 +70,7 @@ describe("test the outgoing webhook action", () => { .head("/", body => body === "") .reply(200) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .n8n({ url: "http://www.example.com", method: HttpMethod.HEAD, diff --git a/packages/server/src/automations/tests/steps/openai.spec.ts b/packages/server/src/automations/tests/steps/openai.spec.ts index 7e9fa1f933..4fbf0aa6d6 100644 --- a/packages/server/src/automations/tests/steps/openai.spec.ts +++ b/packages/server/src/automations/tests/steps/openai.spec.ts @@ -57,7 +57,7 @@ describe("test the openai action", () => { // means it goes through the "legacy" path which requires you to set your // own API key. We don't count this against your quota. const result = await expectAIUsage(0, () => - createAutomationBuilder({ config }) + createAutomationBuilder(config) .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run() ) @@ -68,7 +68,7 @@ describe("test the openai action", () => { it("should present the correct error message when a prompt is not provided", async () => { const result = await expectAIUsage(0, () => - createAutomationBuilder({ config }) + createAutomationBuilder(config) .openai({ prompt: "", model: Model.GPT_4O_MINI }) .run() ) @@ -83,7 +83,7 @@ describe("test the openai action", () => { mockChatGPTError() const result = await expectAIUsage(0, () => - createAutomationBuilder({ config }) + createAutomationBuilder(config) .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run() ) @@ -105,7 +105,7 @@ describe("test the openai action", () => { // calculation we use to approximate cost. This uses Budibase's OpenAI API // key, so we charge users for it. const result = await expectAIUsage(14, () => - createAutomationBuilder({ config }) + createAutomationBuilder(config) .openai({ model: Model.GPT_4O_MINI, prompt: "Hello, world" }) .run() ) diff --git a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts index 9ef3cf1e57..b1d13c6917 100644 --- a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts +++ b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts @@ -23,7 +23,7 @@ describe("test the outgoing webhook action", () => { .post("/", { a: 1 }) .reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .outgoingWebhook({ requestMethod: RequestType.POST, url: "http://www.example.com", @@ -38,7 +38,7 @@ describe("test the outgoing webhook action", () => { }) it("should return an error if something goes wrong in fetch", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .outgoingWebhook({ requestMethod: RequestType.GET, url: "www.invalid.com", diff --git a/packages/server/src/automations/tests/steps/queryRows.spec.ts b/packages/server/src/automations/tests/steps/queryRows.spec.ts index 1d79d8e046..9cda9c94c0 100644 --- a/packages/server/src/automations/tests/steps/queryRows.spec.ts +++ b/packages/server/src/automations/tests/steps/queryRows.spec.ts @@ -28,10 +28,7 @@ describe("Test a query step automation", () => { }) it("should be able to run the query step", async () => { - const result = await createAutomationBuilder({ - name: "Basic Query Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -55,10 +52,7 @@ describe("Test a query step automation", () => { }) it("Returns all rows when onEmptyFilter has no value and no filters are passed", async () => { - const result = await createAutomationBuilder({ - name: "Empty Filter Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -78,10 +72,7 @@ describe("Test a query step automation", () => { }) it("Returns no rows when onEmptyFilter is RETURN_NONE and theres no filters", async () => { - const result = await createAutomationBuilder({ - name: "Return None Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -102,10 +93,7 @@ describe("Test a query step automation", () => { }) it("Returns no rows when onEmptyFilters RETURN_NONE and a filter is passed with a null value", async () => { - const result = await createAutomationBuilder({ - name: "Null Filter Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -130,10 +118,7 @@ describe("Test a query step automation", () => { }) it("Returns rows when onEmptyFilter is RETURN_ALL and no filter is passed", async () => { - const result = await createAutomationBuilder({ - name: "Return All Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: table._id!, @@ -160,10 +145,7 @@ describe("Test a query step automation", () => { await config.api.row.save(tableWithSpaces._id!, { name: NAME, }) - const result = await createAutomationBuilder({ - name: "Return All Test", - config, - }) + const result = await createAutomationBuilder(config) .queryRows( { tableId: tableWithSpaces._id!, diff --git a/packages/server/src/automations/tests/steps/serverLog.spec.ts b/packages/server/src/automations/tests/steps/serverLog.spec.ts index 73d72b9a99..44a9f068b1 100644 --- a/packages/server/src/automations/tests/steps/serverLog.spec.ts +++ b/packages/server/src/automations/tests/steps/serverLog.spec.ts @@ -13,7 +13,7 @@ describe("test the server log action", () => { }) it("should be able to log the text", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .serverLog({ text: "Hello World" }) .run() expect(result.steps[0].outputs.message).toEqual( diff --git a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts index 4047c23566..ef851bc047 100644 --- a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts +++ b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts @@ -17,11 +17,11 @@ describe("Test triggering an automation from another automation", () => { }) it("should trigger an other server log automation", async () => { - const automation = await createAutomationBuilder({ config }) + const automation = await createAutomationBuilder(config) .serverLog({ text: "Hello World" }) .save() - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .triggerAutomationRun({ automation: { automationId: automation._id!, @@ -34,7 +34,7 @@ describe("Test triggering an automation from another automation", () => { }) it("should fail gracefully if the automation id is incorrect", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .triggerAutomationRun({ automation: { // @ts-expect-error - incorrect on purpose diff --git a/packages/server/src/automations/tests/steps/updateRow.spec.ts b/packages/server/src/automations/tests/steps/updateRow.spec.ts index ca3854f745..32c7b90446 100644 --- a/packages/server/src/automations/tests/steps/updateRow.spec.ts +++ b/packages/server/src/automations/tests/steps/updateRow.spec.ts @@ -30,7 +30,7 @@ describe("test the update row action", () => { }) it("should be able to run the update row action", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .updateRow({ rowId: row._id!, row: { @@ -52,7 +52,7 @@ describe("test the update row action", () => { }) it("should check invalid inputs return an error", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .updateRow({ meta: {}, row: {}, rowId: "" }) .run() @@ -60,7 +60,7 @@ describe("test the update row action", () => { }) it("should return an error when table doesn't exist", async () => { - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .updateRow({ row: { _id: "invalid" }, rowId: "invalid", @@ -103,7 +103,7 @@ describe("test the update row action", () => { user2: [{ _id: user2._id }], }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .updateRow({ rowId: row._id!, row: { @@ -156,7 +156,7 @@ describe("test the update row action", () => { user2: [{ _id: user2._id }], }) - const results = await createAutomationBuilder({ config }) + const results = await createAutomationBuilder(config) .updateRow({ rowId: row._id!, row: { diff --git a/packages/server/src/automations/tests/steps/zapier.spec.ts b/packages/server/src/automations/tests/steps/zapier.spec.ts index 331acb68f3..e897083d18 100644 --- a/packages/server/src/automations/tests/steps/zapier.spec.ts +++ b/packages/server/src/automations/tests/steps/zapier.spec.ts @@ -20,7 +20,7 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .zapier({ url: "http://www.example.com", body: null }) .run() @@ -43,7 +43,7 @@ describe("test the outgoing webhook action", () => { .post("/", { ...payload, platform: "budibase" }) .reply(200, { foo: "bar" }) - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .zapier({ url: "http://www.example.com", body: { value: JSON.stringify(payload) }, @@ -55,7 +55,7 @@ describe("test the outgoing webhook action", () => { }) it("should return a 400 if the JSON payload string is malformed", async () => { - const result = await createAutomationBuilder({ config }) + const result = await createAutomationBuilder(config) .zapier({ url: "http://www.example.com", body: { value: "{ invalid json }" }, diff --git a/packages/server/src/automations/tests/triggers/cron.spec.ts b/packages/server/src/automations/tests/triggers/cron.spec.ts index baddb1dd51..84fe90c314 100644 --- a/packages/server/src/automations/tests/triggers/cron.spec.ts +++ b/packages/server/src/automations/tests/triggers/cron.spec.ts @@ -24,7 +24,7 @@ describe("cron trigger", () => { }) }) - await createAutomationBuilder({ config }) + await createAutomationBuilder(config) .cron({ cron: "* * * * *" }) .serverLog({ text: "Hello, world!", @@ -44,7 +44,7 @@ describe("cron trigger", () => { }) it("should fail if the cron expression is invalid", async () => { - await createAutomationBuilder({ config }) + await createAutomationBuilder(config) .cron({ cron: "* * * * * *" }) .serverLog({ text: "Hello, world!", diff --git a/packages/server/src/automations/tests/triggers/webhook.spec.ts b/packages/server/src/automations/tests/triggers/webhook.spec.ts index a0b5e7f195..61fab1e891 100644 --- a/packages/server/src/automations/tests/triggers/webhook.spec.ts +++ b/packages/server/src/automations/tests/triggers/webhook.spec.ts @@ -11,7 +11,7 @@ describe("Branching automations", () => { let webhook: Webhook async function createWebhookAutomation() { - const automation = await createAutomationBuilder({ config }) + const automation = await createAutomationBuilder(config) .webhook({ fields: { parameter: "string" } }) .createRow({ row: { tableId: table._id!, name: "{{ trigger.parameter }}" }, diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index e059bd1c04..5cb982eb9f 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -135,14 +135,12 @@ class AutomationBuilder extends BaseStepBuilder { private triggerOutputs: TriggerOutputs private triggerSet = false - constructor( - options: { name?: string; appId?: string; config?: TestConfiguration } = {} - ) { + constructor(config?: TestConfiguration) { super() - this.config = options.config || setup.getConfig() + this.config = config || setup.getConfig() this.triggerOutputs = { fields: {} } this.automationConfig = { - name: options.name || `Test Automation ${uuidv4()}`, + name: `Test Automation ${uuidv4()}`, definition: { steps: [], trigger: { @@ -154,10 +152,15 @@ class AutomationBuilder extends BaseStepBuilder { stepNames: {}, }, type: "automation", - appId: options.appId ?? this.config.getAppId(), + appId: this.config.getAppId(), } } + name(n: string): this { + this.automationConfig.name = n + return this + } + protected triggerInputOutput< TStep extends AutomationTriggerStepId, TInput = AutomationTriggerInputs, @@ -195,10 +198,13 @@ class AutomationBuilder extends BaseStepBuilder { } } + // The input and output for appAction is identical, and we only ever seem to + // set the output, so we're ignoring the input for now. + appAction = this.triggerOutputOnly(AutomationTriggerStepId.APP) + rowSaved = this.triggerInputOutput(AutomationTriggerStepId.ROW_SAVED) rowUpdated = this.triggerInputOutput(AutomationTriggerStepId.ROW_UPDATED) rowDeleted = this.triggerInputOutput(AutomationTriggerStepId.ROW_DELETED) - appAction = this.triggerOutputOnly(AutomationTriggerStepId.APP) webhook = this.triggerInputOutput(AutomationTriggerStepId.WEBHOOK) cron = this.triggerInputOutput(AutomationTriggerStepId.CRON) @@ -214,9 +220,6 @@ class AutomationBuilder extends BaseStepBuilder { } async save() { - if (!Object.keys(this.automationConfig.definition.trigger).length) { - throw new Error("Please add a trigger to this automation test") - } this.automationConfig.definition.steps = this.steps const { automation } = await this.config.api.automation.post(this.build()) return automation @@ -241,10 +244,6 @@ class AutomationBuilder extends BaseStepBuilder { } } -export function createAutomationBuilder(options?: { - name?: string - appId?: string - config?: TestConfiguration -}) { - return new AutomationBuilder(options) +export function createAutomationBuilder(config?: TestConfiguration) { + return new AutomationBuilder(config) } From 00b02b1762b0b31d6f155948ddcb6ddb113e1150 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 5 Feb 2025 17:45:35 +0000 Subject: [PATCH 04/11] Make config required. --- .../src/automations/tests/utilities/AutomationTestBuilder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 5cb982eb9f..16a049c556 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -244,6 +244,6 @@ class AutomationBuilder extends BaseStepBuilder { } } -export function createAutomationBuilder(config?: TestConfiguration) { +export function createAutomationBuilder(config: TestConfiguration) { return new AutomationBuilder(config) } From 3139fccf2f55e4fc780a9ce19e2de2e2614a8491 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 11:52:41 +0000 Subject: [PATCH 05/11] Refactor automation builder into single class. --- .../tests/utilities/AutomationTestBuilder.ts | 87 ++++++++----------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 16a049c556..2ce2ca3f2e 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -22,7 +22,6 @@ import { WebhookTriggerOutputs, } from "@budibase/types" import TestConfiguration from "../../../tests/utilities/TestConfiguration" -import * as setup from "../utilities" import { automations } from "@budibase/shared-core" type TriggerOutputs = @@ -34,7 +33,7 @@ type TriggerOutputs = | CronTriggerOutputs | undefined -type StepBuilderFunction = (stepBuilder: StepBuilder) => void +type StepBuilderFunction = (stepBuilder: AutomationBuilder) => void type BranchConfig = { [key: string]: { @@ -43,9 +42,38 @@ type BranchConfig = { } } -class BaseStepBuilder { - protected steps: AutomationStep[] = [] - protected stepNames: { [key: string]: string } = {} +class AutomationBuilder { + private automationConfig: Automation + private triggerOutputs: TriggerOutputs + private triggerSet = false + private config: TestConfiguration + private steps: AutomationStep[] = [] + private stepNames: { [key: string]: string } = {} + + constructor(config: TestConfiguration) { + this.config = config + this.triggerOutputs = { fields: {} } + this.automationConfig = { + name: `Test Automation ${uuidv4()}`, + definition: { + steps: [], + trigger: { + ...TRIGGER_DEFINITIONS[AutomationTriggerStepId.APP], + stepId: AutomationTriggerStepId.APP, + inputs: this.triggerOutputs, + id: uuidv4(), + }, + stepNames: {}, + }, + type: "automation", + appId: this.config.getAppId(), + } + } + + name(n: string): this { + this.automationConfig.name = n + return this + } protected createStepFn(stepId: TStep) { return ( @@ -98,7 +126,7 @@ class BaseStepBuilder { } Object.entries(branchConfig).forEach(([key, branch]) => { - const stepBuilder = new StepBuilder() + const stepBuilder = new AutomationBuilder(this.config) branch.steps(stepBuilder) let branchId = uuidv4() branchStepInputs.branches.push({ @@ -106,7 +134,7 @@ class BaseStepBuilder { condition: branch.condition, id: branchId, }) - branchStepInputs.children![branchId] = stepBuilder.build() + branchStepInputs.children![branchId] = stepBuilder.steps }) const branchStep: AutomationStep = { ...automations.steps.branch.definition, @@ -116,51 +144,11 @@ class BaseStepBuilder { } this.steps.push(branchStep) } -} - -class StepBuilder extends BaseStepBuilder { - build(): AutomationStep[] { - return this.steps - } branch(branchConfig: BranchConfig): this { this.addBranchStep(branchConfig) return this } -} - -class AutomationBuilder extends BaseStepBuilder { - private automationConfig: Automation - private config: TestConfiguration - private triggerOutputs: TriggerOutputs - private triggerSet = false - - constructor(config?: TestConfiguration) { - super() - this.config = config || setup.getConfig() - this.triggerOutputs = { fields: {} } - this.automationConfig = { - name: `Test Automation ${uuidv4()}`, - definition: { - steps: [], - trigger: { - ...TRIGGER_DEFINITIONS[AutomationTriggerStepId.APP], - stepId: AutomationTriggerStepId.APP, - inputs: this.triggerOutputs, - id: uuidv4(), - }, - stepNames: {}, - }, - type: "automation", - appId: this.config.getAppId(), - } - } - - name(n: string): this { - this.automationConfig.name = n - return this - } - protected triggerInputOutput< TStep extends AutomationTriggerStepId, TInput = AutomationTriggerInputs, @@ -208,11 +196,6 @@ class AutomationBuilder extends BaseStepBuilder { webhook = this.triggerInputOutput(AutomationTriggerStepId.WEBHOOK) cron = this.triggerInputOutput(AutomationTriggerStepId.CRON) - branch(branchConfig: BranchConfig): this { - this.addBranchStep(branchConfig) - return this - } - build(): Automation { this.automationConfig.definition.steps = this.steps this.automationConfig.definition.stepNames = this.stepNames From ac2f40e9551811e87d8c15fbb9608acce26272b3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 15:46:02 +0000 Subject: [PATCH 06/11] Move trigger output to the run function so it's easier to understand its purpose. --- .../src/api/routes/tests/automation.spec.ts | 11 +- .../src/automations/tests/branching.spec.ts | 27 +-- .../src/automations/tests/scenarios.spec.ts | 56 ++--- .../tests/steps/executeScript.spec.ts | 18 +- .../automations/tests/steps/openai.spec.ts | 12 +- .../automations/tests/steps/queryRows.spec.ts | 18 +- .../tests/triggers/webhook.spec.ts | 2 +- .../tests/utilities/AutomationTestBuilder.ts | 210 ++++++++---------- .../app/automation/StepInputsOutputs.ts | 4 - .../src/documents/app/automation/schema.ts | 3 +- 10 files changed, 176 insertions(+), 185 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index 5b451eef4a..7a86548278 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -108,7 +108,7 @@ describe("/automations", () => { it("Should ensure you can't have a branch as not a last step", async () => { const automation = createAutomationBuilder(config) - .appAction({ fields: { status: "active" } }) + .appAction() .branch({ activeBranch: { steps: stepBuilder => @@ -132,7 +132,7 @@ describe("/automations", () => { it("Should check validation on an automation that has a branch step with no children", async () => { const automation = createAutomationBuilder(config) - .appAction({ fields: { status: "active" } }) + .appAction() .branch({}) .serverLog({ text: "Inactive user" }) .build() @@ -148,7 +148,7 @@ describe("/automations", () => { it("Should check validation on a branch step with empty conditions", async () => { const automation = createAutomationBuilder(config) - .appAction({ fields: { status: "active" } }) + .appAction() .branch({ activeBranch: { steps: stepBuilder => @@ -169,7 +169,7 @@ describe("/automations", () => { it("Should check validation on an branch that has a condition that is not valid", async () => { const automation = createAutomationBuilder(config) - .appAction({ fields: { status: "active" } }) + .appAction() .branch({ activeBranch: { steps: stepBuilder => @@ -241,6 +241,7 @@ describe("/automations", () => { it("should be able to access platformUrl, logoUrl and company in the automation", async () => { const result = await createAutomationBuilder(config) + .appAction() .serverLog({ text: "{{ settings.url }}", }) @@ -250,7 +251,7 @@ describe("/automations", () => { .serverLog({ text: "{{ settings.company }}", }) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.message).toEndWith("https://example.com") expect(result.steps[1].outputs.message).toEndWith( diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index d9fad543f6..da3f10ec53 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -25,6 +25,7 @@ describe("Branching automations", () => { const branch2Id = "44444444-4444-4444-4444-444444444444" const results = await createAutomationBuilder(config) + .appAction() .serverLog( { text: "Starting automation" }, { stepName: "FirstLog", stepId: firstLogId } @@ -75,7 +76,7 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: {} }) expect(results.steps[3].outputs.status).toContain("branch1 branch taken") expect(results.steps[4].outputs.message).toContain("Branch 1.1") @@ -83,7 +84,7 @@ describe("Branching automations", () => { it("should execute correct branch based on string equality", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { status: "active" } }) + .appAction() .branch({ activeBranch: { steps: stepBuilder => stepBuilder.serverLog({ text: "Active user" }), @@ -99,7 +100,7 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { status: "active" } }) expect(results.steps[0].outputs.status).toContain( "activeBranch branch taken" ) @@ -108,7 +109,7 @@ describe("Branching automations", () => { it("should handle multiple conditions with AND operator", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { status: "active", role: "admin" } }) + .appAction() .branch({ activeAdminBranch: { steps: stepBuilder => @@ -129,14 +130,14 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { status: "active", role: "admin" } }) expect(results.steps[1].outputs.message).toContain("Active admin user") }) it("should handle multiple conditions with OR operator", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { status: "test", role: "user" } }) + .appAction() .branch({ specialBranch: { steps: stepBuilder => stepBuilder.serverLog({ text: "Special user" }), @@ -161,14 +162,14 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { status: "test", role: "user" } }) expect(results.steps[1].outputs.message).toContain("Special user") }) it("should stop the branch automation when no conditions are met", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { status: "test", role: "user" } }) + .appAction() .createRow({ row: { name: "Test", tableId: table._id } }) .branch({ specialBranch: { @@ -194,7 +195,7 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { status: "test", role: "user" } }) expect(results.steps[1].outputs.status).toEqual( AutomationStatus.NO_CONDITION_MET @@ -204,7 +205,7 @@ describe("Branching automations", () => { it("evaluate multiple conditions", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { test_trigger: true } }) + .appAction() .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ specialBranch: { @@ -238,14 +239,14 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { test_trigger: true } }) expect(results.steps[2].outputs.message).toContain("Special user") }) it("evaluate multiple conditions with interpolated text", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { test_trigger: true } }) + .appAction() .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ specialBranch: { @@ -275,7 +276,7 @@ describe("Branching automations", () => { }, }, }) - .run() + .run({ fields: { test_trigger: true } }) expect(results.steps[2].outputs.message).toContain("Special user") }) diff --git a/packages/server/src/automations/tests/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios.spec.ts index 2c13b7c019..c831d6f203 100644 --- a/packages/server/src/automations/tests/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios.spec.ts @@ -30,13 +30,7 @@ describe("Automation Scenarios", () => { const table = await config.api.table.save(basicTable()) const results = await createAutomationBuilder(config) - .rowUpdated( - { tableId: table._id! }, - { - row: { name: "Test", description: "TEST" }, - id: "1234", - } - ) + .rowUpdated({ tableId: table._id! }) .createRow({ row: { name: "{{trigger.row.name}}", @@ -44,7 +38,10 @@ describe("Automation Scenarios", () => { tableId: table._id, }, }) - .run() + .run({ + row: { name: "Test", description: "TEST" }, + id: "1234", + }) expect(results.steps).toHaveLength(1) @@ -66,10 +63,11 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) + .appAction() .queryRows({ tableId: table._id!, }) - .run() + .run({ fields: {} }) expect(results.steps).toHaveLength(1) expect(results.steps[0].outputs.rows).toHaveLength(2) @@ -84,6 +82,7 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) + .appAction() .queryRows({ tableId: table._id!, }) @@ -94,7 +93,7 @@ describe("Automation Scenarios", () => { .queryRows({ tableId: table._id!, }) - .run() + .run({ fields: {} }) expect(results.steps).toHaveLength(3) expect(results.steps[1].outputs.success).toBeTruthy() @@ -125,6 +124,7 @@ describe("Automation Scenarios", () => { }) const results = await createAutomationBuilder(config) + .appAction() .createRow( { row: { @@ -153,7 +153,7 @@ describe("Automation Scenarios", () => { }, { stepName: "QueryRowsStep" } ) - .run() + .run({ fields: {} }) expect(results.steps).toHaveLength(3) @@ -193,6 +193,7 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -206,7 +207,7 @@ describe("Automation Scenarios", () => { .queryRows({ tableId: table._id!, }) - .run() + .run({ fields: {} }) expect(results.steps).toHaveLength(3) expect(results.steps[1].outputs.success).toBeTruthy() @@ -242,6 +243,7 @@ describe("Automation Scenarios", () => { it("should stop an automation if the condition is not met", async () => { const results = await createAutomationBuilder(config) + .appAction() .createRow({ row: { name: "Equal Test", @@ -258,7 +260,7 @@ describe("Automation Scenarios", () => { value: 20, }) .serverLog({ text: "Equal condition met" }) - .run() + .run({ fields: {} }) expect(results.steps[2].outputs.success).toBeTrue() expect(results.steps[2].outputs.result).toBeFalse() @@ -267,6 +269,7 @@ describe("Automation Scenarios", () => { it("should continue the automation if the condition is met", async () => { const results = await createAutomationBuilder(config) + .appAction() .createRow({ row: { name: "Not Equal Test", @@ -283,7 +286,7 @@ describe("Automation Scenarios", () => { value: 20, }) .serverLog({ text: "Not Equal condition met" }) - .run() + .run({ fields: {} }) expect(results.steps[2].outputs.success).toBeTrue() expect(results.steps[2].outputs.result).toBeTrue() @@ -333,6 +336,7 @@ describe("Automation Scenarios", () => { "should pass the filter when condition is $condition", async ({ condition, value, rowValue, expectPass }) => { const results = await createAutomationBuilder(config) + .appAction() .createRow({ row: { name: `${condition} Test`, @@ -351,7 +355,7 @@ describe("Automation Scenarios", () => { .serverLog({ text: `${condition} condition ${expectPass ? "passed" : "failed"}`, }) - .run() + .run({ fields: {} }) expect(results.steps[2].outputs.result).toBe(expectPass) if (expectPass) { @@ -367,23 +371,21 @@ describe("Automation Scenarios", () => { const table = await config.api.table.save(basicTable()) const results = await createAutomationBuilder(config) - .rowUpdated( - { tableId: table._id! }, - { - row: { name: "Test", description: "TEST" }, - id: "1234", - } - ) + .rowUpdated({ tableId: table._id! }) .serverLog({ text: "{{ [user].[email] }}" }) - .run() + .run({ + row: { name: "Test", description: "TEST" }, + id: "1234", + }) expect(results.steps[0].outputs.message).toContain("example.com") }) it("Check user is passed through from app trigger", async () => { const results = await createAutomationBuilder(config) + .appAction() .serverLog({ text: "{{ [user].[email] }}" }) - .run() + .run({ fields: {} }) expect(results.steps[0].outputs.message).toContain("example.com") }) @@ -453,9 +455,7 @@ if (descriptions.length) { }) const results = await createAutomationBuilder(config) - .appAction({ - fields: {}, - }) + .appAction() .executeQuery({ query: { queryId: query._id!, @@ -475,7 +475,7 @@ if (descriptions.length) { .queryRows({ tableId: newTable._id!, }) - .run() + .run({ fields: {} }) expect(results.steps).toHaveLength(3) diff --git a/packages/server/src/automations/tests/steps/executeScript.spec.ts b/packages/server/src/automations/tests/steps/executeScript.spec.ts index e2bea9d0c1..9f9b4e71f4 100644 --- a/packages/server/src/automations/tests/steps/executeScript.spec.ts +++ b/packages/server/src/automations/tests/steps/executeScript.spec.ts @@ -21,30 +21,32 @@ describe("Execute Script Automations", () => { it("should execute a basic script and return the result", async () => { const results = await createAutomationBuilder(config) + .appAction() .executeScript({ code: "return 2 + 2" }) - .run() + .run({ fields: {} }) expect(results.steps[0].outputs.value).toEqual(4) }) it("should access bindings from previous steps", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { data: [1, 2, 3] } }) + .appAction() .executeScript( { code: "return trigger.fields.data.map(x => x * 2)", }, { stepId: "binding-script-step" } ) - .run() + .run({ fields: { data: [1, 2, 3] } }) expect(results.steps[0].outputs.value).toEqual([2, 4, 6]) }) it("should handle script execution errors gracefully", async () => { const results = await createAutomationBuilder(config) + .appAction() .executeScript({ code: "return nonexistentVariable.map(x => x)" }) - .run() + .run({ fields: {} }) expect(results.steps[0].outputs.response).toContain( "ReferenceError: nonexistentVariable is not defined" @@ -54,7 +56,7 @@ describe("Execute Script Automations", () => { it("should handle conditional logic in scripts", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: { value: 10 } }) + .appAction() .executeScript({ code: ` if (trigger.fields.value > 5) { @@ -64,14 +66,14 @@ describe("Execute Script Automations", () => { } `, }) - .run() + .run({ fields: { value: 10 } }) expect(results.steps[0].outputs.value).toEqual("Value is greater than 5") }) it("should use multiple steps and validate script execution", async () => { const results = await createAutomationBuilder(config) - .appAction({ fields: {} }) + .appAction() .serverLog( { text: "Starting multi-step automation" }, { stepId: "start-log-step" } @@ -92,7 +94,7 @@ describe("Execute Script Automations", () => { .serverLog({ text: `Final result is {{ steps.ScriptingStep1.value }}`, }) - .run() + .run({ fields: {} }) expect(results.steps[0].outputs.message).toContain( "Starting multi-step automation" diff --git a/packages/server/src/automations/tests/steps/openai.spec.ts b/packages/server/src/automations/tests/steps/openai.spec.ts index 4fbf0aa6d6..eef4bca25b 100644 --- a/packages/server/src/automations/tests/steps/openai.spec.ts +++ b/packages/server/src/automations/tests/steps/openai.spec.ts @@ -58,8 +58,9 @@ describe("test the openai action", () => { // own API key. We don't count this against your quota. const result = await expectAIUsage(0, () => createAutomationBuilder(config) + .appAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) - .run() + .run({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual("This is a test") @@ -69,8 +70,9 @@ describe("test the openai action", () => { it("should present the correct error message when a prompt is not provided", async () => { const result = await expectAIUsage(0, () => createAutomationBuilder(config) + .appAction() .openai({ prompt: "", model: Model.GPT_4O_MINI }) - .run() + .run({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual( @@ -84,8 +86,9 @@ describe("test the openai action", () => { const result = await expectAIUsage(0, () => createAutomationBuilder(config) + .appAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) - .run() + .run({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual( @@ -106,8 +109,9 @@ describe("test the openai action", () => { // key, so we charge users for it. const result = await expectAIUsage(14, () => createAutomationBuilder(config) + .appAction() .openai({ model: Model.GPT_4O_MINI, prompt: "Hello, world" }) - .run() + .run({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual("This is a test") diff --git a/packages/server/src/automations/tests/steps/queryRows.spec.ts b/packages/server/src/automations/tests/steps/queryRows.spec.ts index 9cda9c94c0..7d54590987 100644 --- a/packages/server/src/automations/tests/steps/queryRows.spec.ts +++ b/packages/server/src/automations/tests/steps/queryRows.spec.ts @@ -29,6 +29,7 @@ describe("Test a query step automation", () => { it("should be able to run the query step", async () => { const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -43,7 +44,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query All Rows" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -53,6 +54,7 @@ describe("Test a query step automation", () => { it("Returns all rows when onEmptyFilter has no value and no filters are passed", async () => { const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -63,7 +65,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Empty Filter" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -73,6 +75,7 @@ describe("Test a query step automation", () => { it("Returns no rows when onEmptyFilter is RETURN_NONE and theres no filters", async () => { const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -85,7 +88,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Return None" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -94,6 +97,7 @@ describe("Test a query step automation", () => { it("Returns no rows when onEmptyFilters RETURN_NONE and a filter is passed with a null value", async () => { const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -110,7 +114,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Null Filter" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -119,6 +123,7 @@ describe("Test a query step automation", () => { it("Returns rows when onEmptyFilter is RETURN_ALL and no filter is passed", async () => { const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: table._id!, @@ -130,7 +135,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Return All" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -146,6 +151,7 @@ describe("Test a query step automation", () => { name: NAME, }) const result = await createAutomationBuilder(config) + .appAction() .queryRows( { tableId: tableWithSpaces._id!, @@ -154,7 +160,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query table with spaces" } ) - .run() + .run({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() expect(result.steps[0].outputs.rows.length).toBe(1) diff --git a/packages/server/src/automations/tests/triggers/webhook.spec.ts b/packages/server/src/automations/tests/triggers/webhook.spec.ts index 61fab1e891..e549a6f496 100644 --- a/packages/server/src/automations/tests/triggers/webhook.spec.ts +++ b/packages/server/src/automations/tests/triggers/webhook.spec.ts @@ -11,7 +11,7 @@ describe("Branching automations", () => { let webhook: Webhook async function createWebhookAutomation() { - const automation = await createAutomationBuilder(config) + const { automation } = await createAutomationBuilder(config) .webhook({ fields: { parameter: "string" } }) .createRow({ row: { tableId: table._id!, name: "{{ trigger.parameter }}" }, diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 2ce2ca3f2e..b29124ad0e 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -2,38 +2,26 @@ import { v4 as uuidv4 } from "uuid" import { BUILTIN_ACTION_DEFINITIONS } from "../../actions" import { TRIGGER_DEFINITIONS } from "../../triggers" import { - AppActionTriggerOutputs, Automation, AutomationActionStepId, AutomationStep, AutomationStepInputs, AutomationTrigger, + AutomationTriggerDefinition, AutomationTriggerInputs, AutomationTriggerOutputs, AutomationTriggerStepId, BranchStepInputs, - CronTriggerOutputs, isDidNotTriggerResponse, - RowCreatedTriggerOutputs, - RowDeletedTriggerOutputs, - RowUpdatedTriggerOutputs, SearchFilters, TestAutomationRequest, - WebhookTriggerOutputs, } from "@budibase/types" import TestConfiguration from "../../../tests/utilities/TestConfiguration" import { automations } from "@budibase/shared-core" -type TriggerOutputs = - | RowCreatedTriggerOutputs - | RowUpdatedTriggerOutputs - | RowDeletedTriggerOutputs - | AppActionTriggerOutputs - | WebhookTriggerOutputs - | CronTriggerOutputs - | undefined - -type StepBuilderFunction = (stepBuilder: AutomationBuilder) => void +type StepBuilderFunction = ( + stepBuilder: BranchStepBuilder +) => void type BranchConfig = { [key: string]: { @@ -42,38 +30,42 @@ type BranchConfig = { } } -class AutomationBuilder { - private automationConfig: Automation - private triggerOutputs: TriggerOutputs - private triggerSet = false +class TriggerBuilder { private config: TestConfiguration - private steps: AutomationStep[] = [] - private stepNames: { [key: string]: string } = {} constructor(config: TestConfiguration) { this.config = config - this.triggerOutputs = { fields: {} } - this.automationConfig = { - name: `Test Automation ${uuidv4()}`, - definition: { - steps: [], - trigger: { - ...TRIGGER_DEFINITIONS[AutomationTriggerStepId.APP], - stepId: AutomationTriggerStepId.APP, - inputs: this.triggerOutputs, - id: uuidv4(), - }, - stepNames: {}, - }, - type: "automation", - appId: this.config.getAppId(), + } + + protected trigger< + TStep extends AutomationTriggerStepId, + TInput = AutomationTriggerInputs + >(stepId: TStep) { + return (inputs: TInput) => { + const definition: AutomationTriggerDefinition = + TRIGGER_DEFINITIONS[stepId] + const trigger: AutomationTrigger = { + ...definition, + stepId, + inputs: (inputs || {}) as any, + id: uuidv4(), + } + return new StepBuilder(this.config, trigger) } } - name(n: string): this { - this.automationConfig.name = n - return this - } + appAction = this.trigger(AutomationTriggerStepId.APP) + + rowSaved = this.trigger(AutomationTriggerStepId.ROW_SAVED) + rowUpdated = this.trigger(AutomationTriggerStepId.ROW_UPDATED) + rowDeleted = this.trigger(AutomationTriggerStepId.ROW_DELETED) + webhook = this.trigger(AutomationTriggerStepId.WEBHOOK) + cron = this.trigger(AutomationTriggerStepId.CRON) +} + +class BranchStepBuilder { + protected steps: AutomationStep[] = [] + protected stepNames: { [key: string]: string } = {} protected createStepFn(stepId: TStep) { return ( @@ -120,113 +112,103 @@ class AutomationBuilder { delay = this.createStepFn(AutomationActionStepId.DELAY) protected addBranchStep(branchConfig: BranchConfig): void { - const branchStepInputs: BranchStepInputs = { + const inputs: BranchStepInputs = { branches: [], children: {}, } - Object.entries(branchConfig).forEach(([key, branch]) => { - const stepBuilder = new AutomationBuilder(this.config) - branch.steps(stepBuilder) - let branchId = uuidv4() - branchStepInputs.branches.push({ - name: key, - condition: branch.condition, - id: branchId, - }) - branchStepInputs.children![branchId] = stepBuilder.steps - }) - const branchStep: AutomationStep = { + for (const [name, branch] of Object.entries(branchConfig)) { + const builder = new BranchStepBuilder() + branch.steps(builder) + let id = uuidv4() + inputs.branches.push({ name, condition: branch.condition, id }) + inputs.children![id] = builder.steps + } + + this.steps.push({ ...automations.steps.branch.definition, id: uuidv4(), stepId: AutomationActionStepId.BRANCH, - inputs: branchStepInputs, - } - this.steps.push(branchStep) + inputs, + }) } branch(branchConfig: BranchConfig): this { this.addBranchStep(branchConfig) return this } - protected triggerInputOutput< - TStep extends AutomationTriggerStepId, - TInput = AutomationTriggerInputs, - TOutput = AutomationTriggerOutputs - >(stepId: TStep) { - return (inputs: TInput, outputs?: TOutput) => { - if (this.triggerSet) { - throw new Error("Only one trigger can be set for an automation.") - } - this.triggerOutputs = outputs as TriggerOutputs | undefined - this.automationConfig.definition.trigger = { - ...TRIGGER_DEFINITIONS[stepId], - stepId, - inputs, - id: uuidv4(), - } as AutomationTrigger - this.triggerSet = true - return this - } +} + +class StepBuilder< + TStep extends AutomationTriggerStepId +> extends BranchStepBuilder { + private config: TestConfiguration + private trigger: AutomationTrigger + private _name: string | undefined = undefined + + constructor(config: TestConfiguration, trigger: AutomationTrigger) { + super() + this.config = config + this.trigger = trigger } - protected triggerOutputOnly< - TStep extends AutomationTriggerStepId, - TOutput = AutomationTriggerOutputs - >(stepId: TStep) { - return (outputs: TOutput) => { - this.triggerOutputs = outputs as TriggerOutputs - this.automationConfig.definition.trigger = { - ...TRIGGER_DEFINITIONS[stepId], - stepId, - id: uuidv4(), - } as AutomationTrigger - this.triggerSet = true - return this - } + name(n: string): this { + this._name = n + return this } - // The input and output for appAction is identical, and we only ever seem to - // set the output, so we're ignoring the input for now. - appAction = this.triggerOutputOnly(AutomationTriggerStepId.APP) - - rowSaved = this.triggerInputOutput(AutomationTriggerStepId.ROW_SAVED) - rowUpdated = this.triggerInputOutput(AutomationTriggerStepId.ROW_UPDATED) - rowDeleted = this.triggerInputOutput(AutomationTriggerStepId.ROW_DELETED) - webhook = this.triggerInputOutput(AutomationTriggerStepId.WEBHOOK) - cron = this.triggerInputOutput(AutomationTriggerStepId.CRON) - build(): Automation { - this.automationConfig.definition.steps = this.steps - this.automationConfig.definition.stepNames = this.stepNames - return this.automationConfig + const name = this._name || `Test Automation ${uuidv4()}` + return { + name, + definition: { + steps: this.steps, + trigger: this.trigger, + stepNames: this.stepNames, + }, + type: "automation", + appId: this.config.getAppId(), + } } async save() { - this.automationConfig.definition.steps = this.steps const { automation } = await this.config.api.automation.post(this.build()) - return automation + return new AutomationRunner(this.config, automation) } - async run() { - const automation = await this.save() + async run(outputs: AutomationTriggerOutputs) { + const runner = await this.save() + return await runner.run(outputs) + } +} + +class AutomationRunner { + private config: TestConfiguration + automation: Automation + + constructor(config: TestConfiguration, automation: Automation) { + this.config = config + this.automation = automation + } + + async run(outputs: AutomationTriggerOutputs) { const response = await this.config.api.automation.test( - automation._id!, - this.triggerOutputs as TestAutomationRequest + this.automation._id!, + // TODO: figure out why this cast is needed. + outputs as TestAutomationRequest ) if (isDidNotTriggerResponse(response)) { throw new Error(response.message) } + // Remove the trigger step from the response. response.steps.shift() - return { - trigger: response.trigger, - steps: response.steps, - } + + return response } } export function createAutomationBuilder(config: TestConfiguration) { - return new AutomationBuilder(config) + return new TriggerBuilder(config) } diff --git a/packages/types/src/documents/app/automation/StepInputsOutputs.ts b/packages/types/src/documents/app/automation/StepInputsOutputs.ts index b9c54cec34..18a6f86284 100644 --- a/packages/types/src/documents/app/automation/StepInputsOutputs.ts +++ b/packages/types/src/documents/app/automation/StepInputsOutputs.ts @@ -253,10 +253,6 @@ export type OutgoingWebhookStepInputs = { headers: string | Record } -export type AppActionTriggerInputs = { - fields: object -} - export type AppActionTriggerOutputs = { fields: object } diff --git a/packages/types/src/documents/app/automation/schema.ts b/packages/types/src/documents/app/automation/schema.ts index 952397b511..66a6f508fe 100644 --- a/packages/types/src/documents/app/automation/schema.ts +++ b/packages/types/src/documents/app/automation/schema.ts @@ -45,7 +45,6 @@ import { OpenAIStepInputs, OpenAIStepOutputs, LoopStepInputs, - AppActionTriggerInputs, CronTriggerInputs, RowUpdatedTriggerInputs, RowCreatedTriggerInputs, @@ -332,7 +331,7 @@ export type AutomationTriggerDefinition = Omit< export type AutomationTriggerInputs = T extends AutomationTriggerStepId.APP - ? AppActionTriggerInputs + ? void : T extends AutomationTriggerStepId.CRON ? CronTriggerInputs : T extends AutomationTriggerStepId.ROW_ACTION From 9f28eb00d4c462822badd12712797f401e9e50bf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 15:47:10 +0000 Subject: [PATCH 07/11] Rename triggers to start with 'on' --- .../src/api/routes/tests/automation.spec.ts | 10 ++++----- .../src/automations/tests/branching.spec.ts | 14 ++++++------ .../src/automations/tests/scenarios.spec.ts | 22 +++++++++---------- .../src/automations/tests/steps/bash.spec.ts | 6 ++--- .../automations/tests/steps/createRow.spec.ts | 12 +++++----- .../tests/steps/cron-automations.spec.ts | 2 +- .../tests/steps/executeScript.spec.ts | 10 ++++----- .../src/automations/tests/steps/loop.spec.ts | 6 ++--- .../automations/tests/steps/openai.spec.ts | 8 +++---- .../automations/tests/steps/queryRows.spec.ts | 12 +++++----- .../automations/tests/triggers/cron.spec.ts | 4 ++-- .../tests/triggers/webhook.spec.ts | 2 +- .../tests/utilities/AutomationTestBuilder.ts | 12 +++++----- 13 files changed, 60 insertions(+), 60 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index 7a86548278..a27c21ded4 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -108,7 +108,7 @@ describe("/automations", () => { it("Should ensure you can't have a branch as not a last step", async () => { const automation = createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ activeBranch: { steps: stepBuilder => @@ -132,7 +132,7 @@ describe("/automations", () => { it("Should check validation on an automation that has a branch step with no children", async () => { const automation = createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({}) .serverLog({ text: "Inactive user" }) .build() @@ -148,7 +148,7 @@ describe("/automations", () => { it("Should check validation on a branch step with empty conditions", async () => { const automation = createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ activeBranch: { steps: stepBuilder => @@ -169,7 +169,7 @@ describe("/automations", () => { it("Should check validation on an branch that has a condition that is not valid", async () => { const automation = createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ activeBranch: { steps: stepBuilder => @@ -241,7 +241,7 @@ describe("/automations", () => { it("should be able to access platformUrl, logoUrl and company in the automation", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog({ text: "{{ settings.url }}", }) diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index da3f10ec53..feac5f80f5 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -25,7 +25,7 @@ describe("Branching automations", () => { const branch2Id = "44444444-4444-4444-4444-444444444444" const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog( { text: "Starting automation" }, { stepName: "FirstLog", stepId: firstLogId } @@ -84,7 +84,7 @@ describe("Branching automations", () => { it("should execute correct branch based on string equality", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ activeBranch: { steps: stepBuilder => stepBuilder.serverLog({ text: "Active user" }), @@ -109,7 +109,7 @@ describe("Branching automations", () => { it("should handle multiple conditions with AND operator", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ activeAdminBranch: { steps: stepBuilder => @@ -137,7 +137,7 @@ describe("Branching automations", () => { it("should handle multiple conditions with OR operator", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .branch({ specialBranch: { steps: stepBuilder => stepBuilder.serverLog({ text: "Special user" }), @@ -169,7 +169,7 @@ describe("Branching automations", () => { it("should stop the branch automation when no conditions are met", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .createRow({ row: { name: "Test", tableId: table._id } }) .branch({ specialBranch: { @@ -205,7 +205,7 @@ describe("Branching automations", () => { it("evaluate multiple conditions", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ specialBranch: { @@ -246,7 +246,7 @@ describe("Branching automations", () => { it("evaluate multiple conditions with interpolated text", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog({ text: "Starting automation" }, { stepId: "aN6znRYHG" }) .branch({ specialBranch: { diff --git a/packages/server/src/automations/tests/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios.spec.ts index c831d6f203..e37aea838e 100644 --- a/packages/server/src/automations/tests/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios.spec.ts @@ -30,7 +30,7 @@ describe("Automation Scenarios", () => { const table = await config.api.table.save(basicTable()) const results = await createAutomationBuilder(config) - .rowUpdated({ tableId: table._id! }) + .onRowUpdated({ tableId: table._id! }) .createRow({ row: { name: "{{trigger.row.name}}", @@ -63,7 +63,7 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows({ tableId: table._id!, }) @@ -82,7 +82,7 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows({ tableId: table._id!, }) @@ -124,7 +124,7 @@ describe("Automation Scenarios", () => { }) const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .createRow( { row: { @@ -193,7 +193,7 @@ describe("Automation Scenarios", () => { await config.api.row.save(table._id!, row) await config.api.row.save(table._id!, row) const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -243,7 +243,7 @@ describe("Automation Scenarios", () => { it("should stop an automation if the condition is not met", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .createRow({ row: { name: "Equal Test", @@ -269,7 +269,7 @@ describe("Automation Scenarios", () => { it("should continue the automation if the condition is met", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .createRow({ row: { name: "Not Equal Test", @@ -336,7 +336,7 @@ describe("Automation Scenarios", () => { "should pass the filter when condition is $condition", async ({ condition, value, rowValue, expectPass }) => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .createRow({ row: { name: `${condition} Test`, @@ -371,7 +371,7 @@ describe("Automation Scenarios", () => { const table = await config.api.table.save(basicTable()) const results = await createAutomationBuilder(config) - .rowUpdated({ tableId: table._id! }) + .onRowUpdated({ tableId: table._id! }) .serverLog({ text: "{{ [user].[email] }}" }) .run({ row: { name: "Test", description: "TEST" }, @@ -383,7 +383,7 @@ describe("Automation Scenarios", () => { it("Check user is passed through from app trigger", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog({ text: "{{ [user].[email] }}" }) .run({ fields: {} }) @@ -455,7 +455,7 @@ if (descriptions.length) { }) const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .executeQuery({ query: { queryId: query._id!, diff --git a/packages/server/src/automations/tests/steps/bash.spec.ts b/packages/server/src/automations/tests/steps/bash.spec.ts index 9b797f9479..2174a6db42 100644 --- a/packages/server/src/automations/tests/steps/bash.spec.ts +++ b/packages/server/src/automations/tests/steps/bash.spec.ts @@ -25,7 +25,7 @@ describe("Execute Bash Automations", () => { it("should use trigger data in bash command and pass output to subsequent steps", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { command: "hello world" } }) + .onAppAction({ fields: { command: "hello world" } }) .bash( { code: "echo '{{ trigger.fields.command }}'" }, { stepName: "Echo Command" } @@ -44,7 +44,7 @@ describe("Execute Bash Automations", () => { it("should chain multiple bash commands using previous outputs", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { filename: "testfile.txt" } }) + .onAppAction({ fields: { filename: "testfile.txt" } }) .bash( { code: "echo 'initial content' > {{ trigger.fields.filename }}" }, { stepName: "Create File" } @@ -94,7 +94,7 @@ describe("Execute Bash Automations", () => { it("should handle bash output in conditional logic", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { threshold: "5" } }) + .onAppAction({ fields: { threshold: "5" } }) .bash( { code: "echo $(( {{ trigger.fields.threshold }} + 5 ))" }, { stepName: "Calculate Value" } diff --git a/packages/server/src/automations/tests/steps/createRow.spec.ts b/packages/server/src/automations/tests/steps/createRow.spec.ts index 4456550cb3..9d2fb5374f 100644 --- a/packages/server/src/automations/tests/steps/createRow.spec.ts +++ b/packages/server/src/automations/tests/steps/createRow.spec.ts @@ -41,7 +41,7 @@ describe("test the create row action", () => { it("should be able to run the action", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { status: "new" } }) + .onAppAction({ fields: { status: "new" } }) .serverLog({ text: "Starting create row flow" }, { stepName: "StartLog" }) .createRow({ row }, { stepName: "CreateRow" }) .serverLog( @@ -67,7 +67,7 @@ describe("test the create row action", () => { it("should return an error (not throw) when bad info provided", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { status: "error" } }) + .onAppAction({ fields: { status: "error" } }) .serverLog({ text: "Starting error test flow" }, { stepName: "StartLog" }) .createRow( { @@ -85,7 +85,7 @@ describe("test the create row action", () => { it("should check invalid inputs return an error", async () => { const result = await createAutomationBuilder(config) - .appAction({ fields: { status: "invalid" } }) + .onAppAction({ fields: { status: "invalid" } }) .serverLog({ text: "Testing invalid input" }, { stepName: "StartLog" }) .createRow({ row: {} }, { stepName: "CreateRow" }) .filter({ @@ -123,7 +123,7 @@ describe("test the create row action", () => { attachmentRow.file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .appAction({ fields: { type: "attachment" } }) + .onAppAction({ fields: { type: "attachment" } }) .serverLog( { text: "Processing attachment upload" }, { stepName: "StartLog" } @@ -174,7 +174,7 @@ describe("test the create row action", () => { attachmentRow.single_file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .appAction({ fields: { type: "single-attachment" } }) + .onAppAction({ fields: { type: "single-attachment" } }) .serverLog( { text: "Processing single attachment" }, { stepName: "StartLog" } @@ -245,7 +245,7 @@ describe("test the create row action", () => { attachmentRow.single_file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .appAction({ fields: { type: "invalid-attachment" } }) + .onAppAction({ fields: { type: "invalid-attachment" } }) .serverLog( { text: "Testing invalid attachment keys" }, { stepName: "StartLog" } diff --git a/packages/server/src/automations/tests/steps/cron-automations.spec.ts b/packages/server/src/automations/tests/steps/cron-automations.spec.ts index ed0729bd38..41de957c52 100644 --- a/packages/server/src/automations/tests/steps/cron-automations.spec.ts +++ b/packages/server/src/automations/tests/steps/cron-automations.spec.ts @@ -27,7 +27,7 @@ describe("cron automations", () => { }) it("should initialise the automation timestamp", async () => { - await createAutomationBuilder(config).cron({ cron: "* * * * *" }).save() + await createAutomationBuilder(config).onCron({ cron: "* * * * *" }).save() tk.travel(Date.now() + oneMinuteInMs) await config.publish() diff --git a/packages/server/src/automations/tests/steps/executeScript.spec.ts b/packages/server/src/automations/tests/steps/executeScript.spec.ts index 9f9b4e71f4..351af09a4a 100644 --- a/packages/server/src/automations/tests/steps/executeScript.spec.ts +++ b/packages/server/src/automations/tests/steps/executeScript.spec.ts @@ -21,7 +21,7 @@ describe("Execute Script Automations", () => { it("should execute a basic script and return the result", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .executeScript({ code: "return 2 + 2" }) .run({ fields: {} }) @@ -30,7 +30,7 @@ describe("Execute Script Automations", () => { it("should access bindings from previous steps", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .executeScript( { code: "return trigger.fields.data.map(x => x * 2)", @@ -44,7 +44,7 @@ describe("Execute Script Automations", () => { it("should handle script execution errors gracefully", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .executeScript({ code: "return nonexistentVariable.map(x => x)" }) .run({ fields: {} }) @@ -56,7 +56,7 @@ describe("Execute Script Automations", () => { it("should handle conditional logic in scripts", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .executeScript({ code: ` if (trigger.fields.value > 5) { @@ -73,7 +73,7 @@ describe("Execute Script Automations", () => { it("should use multiple steps and validate script execution", async () => { const results = await createAutomationBuilder(config) - .appAction() + .onAppAction() .serverLog( { text: "Starting multi-step automation" }, { stepId: "start-log-step" } diff --git a/packages/server/src/automations/tests/steps/loop.spec.ts b/packages/server/src/automations/tests/steps/loop.spec.ts index ba74bf6778..3d53b3aa01 100644 --- a/packages/server/src/automations/tests/steps/loop.spec.ts +++ b/packages/server/src/automations/tests/steps/loop.spec.ts @@ -73,7 +73,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation with a trigger, loop, and create row step", async () => { const results = await createAutomationBuilder(config) - .rowSaved( + .onRowSaved( { tableId: table._id! }, { row: { @@ -116,7 +116,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation where a loop step is between two normal steps to ensure context correctness", async () => { const results = await createAutomationBuilder(config) - .rowSaved( + .onRowSaved( { tableId: table._id! }, { row: { @@ -213,7 +213,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation where a loop is successfully run twice", async () => { const results = await createAutomationBuilder(config) - .rowSaved( + .onRowSaved( { tableId: table._id! }, { row: { diff --git a/packages/server/src/automations/tests/steps/openai.spec.ts b/packages/server/src/automations/tests/steps/openai.spec.ts index eef4bca25b..fb3382cff7 100644 --- a/packages/server/src/automations/tests/steps/openai.spec.ts +++ b/packages/server/src/automations/tests/steps/openai.spec.ts @@ -58,7 +58,7 @@ describe("test the openai action", () => { // own API key. We don't count this against your quota. const result = await expectAIUsage(0, () => createAutomationBuilder(config) - .appAction() + .onAppAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run({ fields: {} }) ) @@ -70,7 +70,7 @@ describe("test the openai action", () => { it("should present the correct error message when a prompt is not provided", async () => { const result = await expectAIUsage(0, () => createAutomationBuilder(config) - .appAction() + .onAppAction() .openai({ prompt: "", model: Model.GPT_4O_MINI }) .run({ fields: {} }) ) @@ -86,7 +86,7 @@ describe("test the openai action", () => { const result = await expectAIUsage(0, () => createAutomationBuilder(config) - .appAction() + .onAppAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) .run({ fields: {} }) ) @@ -109,7 +109,7 @@ describe("test the openai action", () => { // key, so we charge users for it. const result = await expectAIUsage(14, () => createAutomationBuilder(config) - .appAction() + .onAppAction() .openai({ model: Model.GPT_4O_MINI, prompt: "Hello, world" }) .run({ fields: {} }) ) diff --git a/packages/server/src/automations/tests/steps/queryRows.spec.ts b/packages/server/src/automations/tests/steps/queryRows.spec.ts index 7d54590987..b377d05e73 100644 --- a/packages/server/src/automations/tests/steps/queryRows.spec.ts +++ b/packages/server/src/automations/tests/steps/queryRows.spec.ts @@ -29,7 +29,7 @@ describe("Test a query step automation", () => { it("should be able to run the query step", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -54,7 +54,7 @@ describe("Test a query step automation", () => { it("Returns all rows when onEmptyFilter has no value and no filters are passed", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -75,7 +75,7 @@ describe("Test a query step automation", () => { it("Returns no rows when onEmptyFilter is RETURN_NONE and theres no filters", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -97,7 +97,7 @@ describe("Test a query step automation", () => { it("Returns no rows when onEmptyFilters RETURN_NONE and a filter is passed with a null value", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -123,7 +123,7 @@ describe("Test a query step automation", () => { it("Returns rows when onEmptyFilter is RETURN_ALL and no filter is passed", async () => { const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: table._id!, @@ -151,7 +151,7 @@ describe("Test a query step automation", () => { name: NAME, }) const result = await createAutomationBuilder(config) - .appAction() + .onAppAction() .queryRows( { tableId: tableWithSpaces._id!, diff --git a/packages/server/src/automations/tests/triggers/cron.spec.ts b/packages/server/src/automations/tests/triggers/cron.spec.ts index 84fe90c314..28ef49e5f5 100644 --- a/packages/server/src/automations/tests/triggers/cron.spec.ts +++ b/packages/server/src/automations/tests/triggers/cron.spec.ts @@ -25,7 +25,7 @@ describe("cron trigger", () => { }) await createAutomationBuilder(config) - .cron({ cron: "* * * * *" }) + .onCron({ cron: "* * * * *" }) .serverLog({ text: "Hello, world!", }) @@ -45,7 +45,7 @@ describe("cron trigger", () => { it("should fail if the cron expression is invalid", async () => { await createAutomationBuilder(config) - .cron({ cron: "* * * * * *" }) + .onCron({ cron: "* * * * * *" }) .serverLog({ text: "Hello, world!", }) diff --git a/packages/server/src/automations/tests/triggers/webhook.spec.ts b/packages/server/src/automations/tests/triggers/webhook.spec.ts index e549a6f496..664812f860 100644 --- a/packages/server/src/automations/tests/triggers/webhook.spec.ts +++ b/packages/server/src/automations/tests/triggers/webhook.spec.ts @@ -12,7 +12,7 @@ describe("Branching automations", () => { async function createWebhookAutomation() { const { automation } = await createAutomationBuilder(config) - .webhook({ fields: { parameter: "string" } }) + .onWebhook({ fields: { parameter: "string" } }) .createRow({ row: { tableId: table._id!, name: "{{ trigger.parameter }}" }, }) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index b29124ad0e..00f87d3616 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -54,13 +54,13 @@ class TriggerBuilder { } } - appAction = this.trigger(AutomationTriggerStepId.APP) + onAppAction = this.trigger(AutomationTriggerStepId.APP) - rowSaved = this.trigger(AutomationTriggerStepId.ROW_SAVED) - rowUpdated = this.trigger(AutomationTriggerStepId.ROW_UPDATED) - rowDeleted = this.trigger(AutomationTriggerStepId.ROW_DELETED) - webhook = this.trigger(AutomationTriggerStepId.WEBHOOK) - cron = this.trigger(AutomationTriggerStepId.CRON) + onRowSaved = this.trigger(AutomationTriggerStepId.ROW_SAVED) + onRowUpdated = this.trigger(AutomationTriggerStepId.ROW_UPDATED) + onRowDeleted = this.trigger(AutomationTriggerStepId.ROW_DELETED) + onWebhook = this.trigger(AutomationTriggerStepId.WEBHOOK) + onCron = this.trigger(AutomationTriggerStepId.CRON) } class BranchStepBuilder { From 924f4009921572753f17c31660584abd7fadc0f3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 15:58:15 +0000 Subject: [PATCH 08/11] Rename run to test to better reflect the API being hit. --- .../src/api/routes/tests/automation.spec.ts | 2 +- .../src/automations/tests/branching.spec.ts | 14 ++-- .../src/automations/tests/scenarios.spec.ts | 22 +++---- .../src/automations/tests/steps/bash.spec.ts | 6 +- .../automations/tests/steps/createRow.spec.ts | 12 ++-- .../tests/steps/executeScript.spec.ts | 10 +-- .../src/automations/tests/steps/loop.spec.ts | 6 +- .../automations/tests/steps/openai.spec.ts | 8 +-- .../automations/tests/steps/queryRows.spec.ts | 12 ++-- .../tests/utilities/AutomationTestBuilder.ts | 64 +++++++++---------- 10 files changed, 78 insertions(+), 78 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index a27c21ded4..1d7b9cd6ed 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -251,7 +251,7 @@ describe("/automations", () => { .serverLog({ text: "{{ settings.company }}", }) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.message).toEndWith("https://example.com") expect(result.steps[1].outputs.message).toEndWith( diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index feac5f80f5..bf9b9ce3f8 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -76,7 +76,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[3].outputs.status).toContain("branch1 branch taken") expect(results.steps[4].outputs.message).toContain("Branch 1.1") @@ -100,7 +100,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { status: "active" } }) + .test({ fields: { status: "active" } }) expect(results.steps[0].outputs.status).toContain( "activeBranch branch taken" ) @@ -130,7 +130,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { status: "active", role: "admin" } }) + .test({ fields: { status: "active", role: "admin" } }) expect(results.steps[1].outputs.message).toContain("Active admin user") }) @@ -162,7 +162,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { status: "test", role: "user" } }) + .test({ fields: { status: "test", role: "user" } }) expect(results.steps[1].outputs.message).toContain("Special user") }) @@ -195,7 +195,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { status: "test", role: "user" } }) + .test({ fields: { status: "test", role: "user" } }) expect(results.steps[1].outputs.status).toEqual( AutomationStatus.NO_CONDITION_MET @@ -239,7 +239,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { test_trigger: true } }) + .test({ fields: { test_trigger: true } }) expect(results.steps[2].outputs.message).toContain("Special user") }) @@ -276,7 +276,7 @@ describe("Branching automations", () => { }, }, }) - .run({ fields: { test_trigger: true } }) + .test({ fields: { test_trigger: true } }) expect(results.steps[2].outputs.message).toContain("Special user") }) diff --git a/packages/server/src/automations/tests/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios.spec.ts index e37aea838e..3015e75018 100644 --- a/packages/server/src/automations/tests/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios.spec.ts @@ -38,7 +38,7 @@ describe("Automation Scenarios", () => { tableId: table._id, }, }) - .run({ + .test({ row: { name: "Test", description: "TEST" }, id: "1234", }) @@ -67,7 +67,7 @@ describe("Automation Scenarios", () => { .queryRows({ tableId: table._id!, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps).toHaveLength(1) expect(results.steps[0].outputs.rows).toHaveLength(2) @@ -93,7 +93,7 @@ describe("Automation Scenarios", () => { .queryRows({ tableId: table._id!, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps).toHaveLength(3) expect(results.steps[1].outputs.success).toBeTruthy() @@ -153,7 +153,7 @@ describe("Automation Scenarios", () => { }, { stepName: "QueryRowsStep" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps).toHaveLength(3) @@ -207,7 +207,7 @@ describe("Automation Scenarios", () => { .queryRows({ tableId: table._id!, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps).toHaveLength(3) expect(results.steps[1].outputs.success).toBeTruthy() @@ -260,7 +260,7 @@ describe("Automation Scenarios", () => { value: 20, }) .serverLog({ text: "Equal condition met" }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[2].outputs.success).toBeTrue() expect(results.steps[2].outputs.result).toBeFalse() @@ -286,7 +286,7 @@ describe("Automation Scenarios", () => { value: 20, }) .serverLog({ text: "Not Equal condition met" }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[2].outputs.success).toBeTrue() expect(results.steps[2].outputs.result).toBeTrue() @@ -355,7 +355,7 @@ describe("Automation Scenarios", () => { .serverLog({ text: `${condition} condition ${expectPass ? "passed" : "failed"}`, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[2].outputs.result).toBe(expectPass) if (expectPass) { @@ -373,7 +373,7 @@ describe("Automation Scenarios", () => { const results = await createAutomationBuilder(config) .onRowUpdated({ tableId: table._id! }) .serverLog({ text: "{{ [user].[email] }}" }) - .run({ + .test({ row: { name: "Test", description: "TEST" }, id: "1234", }) @@ -385,7 +385,7 @@ describe("Automation Scenarios", () => { const results = await createAutomationBuilder(config) .onAppAction() .serverLog({ text: "{{ [user].[email] }}" }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[0].outputs.message).toContain("example.com") }) @@ -475,7 +475,7 @@ if (descriptions.length) { .queryRows({ tableId: newTable._id!, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps).toHaveLength(3) diff --git a/packages/server/src/automations/tests/steps/bash.spec.ts b/packages/server/src/automations/tests/steps/bash.spec.ts index 2174a6db42..48174ceb04 100644 --- a/packages/server/src/automations/tests/steps/bash.spec.ts +++ b/packages/server/src/automations/tests/steps/bash.spec.ts @@ -34,7 +34,7 @@ describe("Execute Bash Automations", () => { { text: "Bash output was: {{ steps.[Echo Command].stdout }}" }, { stepName: "Log Output" } ) - .run() + .test() expect(result.steps[0].outputs.stdout).toEqual("hello world\n") expect(result.steps[1].outputs.message).toContain( @@ -57,7 +57,7 @@ describe("Execute Bash Automations", () => { { code: "rm {{ trigger.fields.filename }}" }, { stepName: "Cleanup" } ) - .run() + .test() expect(result.steps[1].outputs.stdout).toEqual("INITIAL CONTENT\n") expect(result.steps[1].outputs.success).toEqual(true) @@ -112,7 +112,7 @@ describe("Execute Bash Automations", () => { { text: "Value was {{ steps.[Check Value].value }}" }, { stepName: "Log Result" } ) - .run() + .test() expect(result.steps[0].outputs.stdout).toEqual("10\n") expect(result.steps[1].outputs.value).toEqual("high") diff --git a/packages/server/src/automations/tests/steps/createRow.spec.ts b/packages/server/src/automations/tests/steps/createRow.spec.ts index 9d2fb5374f..a4523e15ef 100644 --- a/packages/server/src/automations/tests/steps/createRow.spec.ts +++ b/packages/server/src/automations/tests/steps/createRow.spec.ts @@ -48,7 +48,7 @@ describe("test the create row action", () => { { text: "Row created with ID: {{ stepsByName.CreateRow.row._id }}" }, { stepName: "CreationLog" } ) - .run() + .test() expect(result.steps[1].outputs.success).toBeDefined() expect(result.steps[1].outputs.id).toBeDefined() @@ -78,7 +78,7 @@ describe("test the create row action", () => { }, { stepName: "CreateRow" } ) - .run() + .test() expect(result.steps[1].outputs.success).toEqual(false) }) @@ -97,7 +97,7 @@ describe("test the create row action", () => { { text: "This log should not appear" }, { stepName: "SkippedLog" } ) - .run() + .test() expect(result.steps[1].outputs.success).toEqual(false) expect(result.steps.length).toBeLessThan(4) @@ -140,7 +140,7 @@ describe("test the create row action", () => { }, { stepName: "UploadLog" } ) - .run() + .test() expect(result.steps[1].outputs.success).toEqual(true) expect(result.steps[1].outputs.row.file_attachment[0]).toHaveProperty("key") @@ -209,7 +209,7 @@ describe("test the create row action", () => { }, }, }) - .run() + .test() expect(result.steps[1].outputs.success).toEqual(true) expect(result.steps[1].outputs.row.single_file_attachment).toHaveProperty( @@ -278,7 +278,7 @@ describe("test the create row action", () => { }, }, }) - .run() + .test() expect(result.steps[1].outputs.success).toEqual(false) expect(result.steps[1].outputs.response).toEqual( diff --git a/packages/server/src/automations/tests/steps/executeScript.spec.ts b/packages/server/src/automations/tests/steps/executeScript.spec.ts index 351af09a4a..117c2341ba 100644 --- a/packages/server/src/automations/tests/steps/executeScript.spec.ts +++ b/packages/server/src/automations/tests/steps/executeScript.spec.ts @@ -23,7 +23,7 @@ describe("Execute Script Automations", () => { const results = await createAutomationBuilder(config) .onAppAction() .executeScript({ code: "return 2 + 2" }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[0].outputs.value).toEqual(4) }) @@ -37,7 +37,7 @@ describe("Execute Script Automations", () => { }, { stepId: "binding-script-step" } ) - .run({ fields: { data: [1, 2, 3] } }) + .test({ fields: { data: [1, 2, 3] } }) expect(results.steps[0].outputs.value).toEqual([2, 4, 6]) }) @@ -46,7 +46,7 @@ describe("Execute Script Automations", () => { const results = await createAutomationBuilder(config) .onAppAction() .executeScript({ code: "return nonexistentVariable.map(x => x)" }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[0].outputs.response).toContain( "ReferenceError: nonexistentVariable is not defined" @@ -66,7 +66,7 @@ describe("Execute Script Automations", () => { } `, }) - .run({ fields: { value: 10 } }) + .test({ fields: { value: 10 } }) expect(results.steps[0].outputs.value).toEqual("Value is greater than 5") }) @@ -94,7 +94,7 @@ describe("Execute Script Automations", () => { .serverLog({ text: `Final result is {{ steps.ScriptingStep1.value }}`, }) - .run({ fields: {} }) + .test({ fields: {} }) expect(results.steps[0].outputs.message).toContain( "Starting multi-step automation" diff --git a/packages/server/src/automations/tests/steps/loop.spec.ts b/packages/server/src/automations/tests/steps/loop.spec.ts index 3d53b3aa01..5e4f3e8126 100644 --- a/packages/server/src/automations/tests/steps/loop.spec.ts +++ b/packages/server/src/automations/tests/steps/loop.spec.ts @@ -95,7 +95,7 @@ describe("Attempt to run a basic loop automation", () => { tableId: table._id, }, }) - .run() + .test() expect(results.trigger).toBeDefined() expect(results.steps).toHaveLength(1) @@ -136,7 +136,7 @@ describe("Attempt to run a basic loop automation", () => { }) .serverLog({ text: "Message {{loop.currentItem}}" }) .serverLog({ text: "{{steps.1.rows.0._id}}" }) - .run() + .test() results.steps[1].outputs.items.forEach( (output: ServerLogStepOutputs, index: number) => { @@ -240,7 +240,7 @@ describe("Attempt to run a basic loop automation", () => { binding: "Message 1,Message 2,Message 3", }) .serverLog({ text: "{{loop.currentItem}}" }) - .run() + .test() expect(results.trigger).toBeDefined() expect(results.steps).toHaveLength(2) diff --git a/packages/server/src/automations/tests/steps/openai.spec.ts b/packages/server/src/automations/tests/steps/openai.spec.ts index fb3382cff7..d5f002571d 100644 --- a/packages/server/src/automations/tests/steps/openai.spec.ts +++ b/packages/server/src/automations/tests/steps/openai.spec.ts @@ -60,7 +60,7 @@ describe("test the openai action", () => { createAutomationBuilder(config) .onAppAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) - .run({ fields: {} }) + .test({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual("This is a test") @@ -72,7 +72,7 @@ describe("test the openai action", () => { createAutomationBuilder(config) .onAppAction() .openai({ prompt: "", model: Model.GPT_4O_MINI }) - .run({ fields: {} }) + .test({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual( @@ -88,7 +88,7 @@ describe("test the openai action", () => { createAutomationBuilder(config) .onAppAction() .openai({ prompt: "Hello, world", model: Model.GPT_4O_MINI }) - .run({ fields: {} }) + .test({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual( @@ -111,7 +111,7 @@ describe("test the openai action", () => { createAutomationBuilder(config) .onAppAction() .openai({ model: Model.GPT_4O_MINI, prompt: "Hello, world" }) - .run({ fields: {} }) + .test({ fields: {} }) ) expect(result.steps[0].outputs.response).toEqual("This is a test") diff --git a/packages/server/src/automations/tests/steps/queryRows.spec.ts b/packages/server/src/automations/tests/steps/queryRows.spec.ts index b377d05e73..f6d756e770 100644 --- a/packages/server/src/automations/tests/steps/queryRows.spec.ts +++ b/packages/server/src/automations/tests/steps/queryRows.spec.ts @@ -44,7 +44,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query All Rows" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -65,7 +65,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Empty Filter" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -88,7 +88,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Return None" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -114,7 +114,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Null Filter" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -135,7 +135,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query With Return All" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() @@ -160,7 +160,7 @@ describe("Test a query step automation", () => { }, { stepName: "Query table with spaces" } ) - .run({ fields: {} }) + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) expect(result.steps[0].outputs.rows).toBeDefined() expect(result.steps[0].outputs.rows.length).toBe(1) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 00f87d3616..835a177ea8 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -31,7 +31,7 @@ type BranchConfig = { } class TriggerBuilder { - private config: TestConfiguration + private readonly config: TestConfiguration constructor(config: TestConfiguration) { this.config = config @@ -64,10 +64,10 @@ class TriggerBuilder { } class BranchStepBuilder { - protected steps: AutomationStep[] = [] - protected stepNames: { [key: string]: string } = {} + protected readonly steps: AutomationStep[] = [] + protected readonly stepNames: { [key: string]: string } = {} - protected createStepFn(stepId: TStep) { + protected step(stepId: TStep) { return ( inputs: AutomationStepInputs, opts?: { stepName?: string; stepId?: string } @@ -88,28 +88,28 @@ class BranchStepBuilder { } } - createRow = this.createStepFn(AutomationActionStepId.CREATE_ROW) - updateRow = this.createStepFn(AutomationActionStepId.UPDATE_ROW) - deleteRow = this.createStepFn(AutomationActionStepId.DELETE_ROW) - sendSmtpEmail = this.createStepFn(AutomationActionStepId.SEND_EMAIL_SMTP) - executeQuery = this.createStepFn(AutomationActionStepId.EXECUTE_QUERY) - queryRows = this.createStepFn(AutomationActionStepId.QUERY_ROWS) - loop = this.createStepFn(AutomationActionStepId.LOOP) - serverLog = this.createStepFn(AutomationActionStepId.SERVER_LOG) - executeScript = this.createStepFn(AutomationActionStepId.EXECUTE_SCRIPT) - filter = this.createStepFn(AutomationActionStepId.FILTER) - bash = this.createStepFn(AutomationActionStepId.EXECUTE_BASH) - openai = this.createStepFn(AutomationActionStepId.OPENAI) - collect = this.createStepFn(AutomationActionStepId.COLLECT) - zapier = this.createStepFn(AutomationActionStepId.zapier) - triggerAutomationRun = this.createStepFn( + createRow = this.step(AutomationActionStepId.CREATE_ROW) + updateRow = this.step(AutomationActionStepId.UPDATE_ROW) + deleteRow = this.step(AutomationActionStepId.DELETE_ROW) + sendSmtpEmail = this.step(AutomationActionStepId.SEND_EMAIL_SMTP) + executeQuery = this.step(AutomationActionStepId.EXECUTE_QUERY) + queryRows = this.step(AutomationActionStepId.QUERY_ROWS) + loop = this.step(AutomationActionStepId.LOOP) + serverLog = this.step(AutomationActionStepId.SERVER_LOG) + executeScript = this.step(AutomationActionStepId.EXECUTE_SCRIPT) + filter = this.step(AutomationActionStepId.FILTER) + bash = this.step(AutomationActionStepId.EXECUTE_BASH) + openai = this.step(AutomationActionStepId.OPENAI) + collect = this.step(AutomationActionStepId.COLLECT) + zapier = this.step(AutomationActionStepId.zapier) + triggerAutomationRun = this.step( AutomationActionStepId.TRIGGER_AUTOMATION_RUN ) - outgoingWebhook = this.createStepFn(AutomationActionStepId.OUTGOING_WEBHOOK) - n8n = this.createStepFn(AutomationActionStepId.n8n) - make = this.createStepFn(AutomationActionStepId.integromat) - discord = this.createStepFn(AutomationActionStepId.discord) - delay = this.createStepFn(AutomationActionStepId.DELAY) + outgoingWebhook = this.step(AutomationActionStepId.OUTGOING_WEBHOOK) + n8n = this.step(AutomationActionStepId.n8n) + make = this.step(AutomationActionStepId.integromat) + discord = this.step(AutomationActionStepId.discord) + delay = this.step(AutomationActionStepId.DELAY) protected addBranchStep(branchConfig: BranchConfig): void { const inputs: BranchStepInputs = { @@ -142,8 +142,8 @@ class BranchStepBuilder { class StepBuilder< TStep extends AutomationTriggerStepId > extends BranchStepBuilder { - private config: TestConfiguration - private trigger: AutomationTrigger + private readonly config: TestConfiguration + private readonly trigger: AutomationTrigger private _name: string | undefined = undefined constructor(config: TestConfiguration, trigger: AutomationTrigger) { @@ -176,26 +176,26 @@ class StepBuilder< return new AutomationRunner(this.config, automation) } - async run(outputs: AutomationTriggerOutputs) { + async test(triggerOutput: AutomationTriggerOutputs) { const runner = await this.save() - return await runner.run(outputs) + return await runner.test(triggerOutput) } } class AutomationRunner { - private config: TestConfiguration - automation: Automation + private readonly config: TestConfiguration + readonly automation: Automation constructor(config: TestConfiguration, automation: Automation) { this.config = config this.automation = automation } - async run(outputs: AutomationTriggerOutputs) { + async test(triggerOutput: AutomationTriggerOutputs) { const response = await this.config.api.automation.test( this.automation._id!, // TODO: figure out why this cast is needed. - outputs as TestAutomationRequest + triggerOutput as TestAutomationRequest ) if (isDidNotTriggerResponse(response)) { From 03e4cfe0b479ef4869df9f00744db78b43d9e278 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 16:44:47 +0000 Subject: [PATCH 09/11] Missed a bunch of spots in the refactor. --- .../src/automations/tests/steps/bash.spec.ts | 18 ++-- .../automations/tests/steps/createRow.spec.ts | 24 ++--- .../src/automations/tests/steps/delay.spec.ts | 5 +- .../automations/tests/steps/deleteRow.spec.ts | 9 +- .../automations/tests/steps/discord.spec.ts | 3 +- .../automations/tests/steps/filter.spec.ts | 6 +- .../src/automations/tests/steps/loop.spec.ts | 90 +++++++++---------- .../src/automations/tests/steps/make.spec.ts | 9 +- .../src/automations/tests/steps/n8n.spec.ts | 12 ++- .../tests/steps/outgoingWebhook.spec.ts | 6 +- .../automations/tests/steps/serverLog.spec.ts | 3 +- .../tests/steps/triggerAutomationRun.spec.ts | 9 +- .../automations/tests/steps/updateRow.spec.ts | 15 ++-- .../automations/tests/steps/zapier.spec.ts | 9 +- 14 files changed, 125 insertions(+), 93 deletions(-) diff --git a/packages/server/src/automations/tests/steps/bash.spec.ts b/packages/server/src/automations/tests/steps/bash.spec.ts index 48174ceb04..a2172c3578 100644 --- a/packages/server/src/automations/tests/steps/bash.spec.ts +++ b/packages/server/src/automations/tests/steps/bash.spec.ts @@ -25,7 +25,7 @@ describe("Execute Bash Automations", () => { it("should use trigger data in bash command and pass output to subsequent steps", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { command: "hello world" } }) + .onAppAction() .bash( { code: "echo '{{ trigger.fields.command }}'" }, { stepName: "Echo Command" } @@ -34,7 +34,7 @@ describe("Execute Bash Automations", () => { { text: "Bash output was: {{ steps.[Echo Command].stdout }}" }, { stepName: "Log Output" } ) - .test() + .test({ fields: { command: "hello world" } }) expect(result.steps[0].outputs.stdout).toEqual("hello world\n") expect(result.steps[1].outputs.message).toContain( @@ -44,7 +44,7 @@ describe("Execute Bash Automations", () => { it("should chain multiple bash commands using previous outputs", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { filename: "testfile.txt" } }) + .onAppAction() .bash( { code: "echo 'initial content' > {{ trigger.fields.filename }}" }, { stepName: "Create File" } @@ -57,7 +57,7 @@ describe("Execute Bash Automations", () => { { code: "rm {{ trigger.fields.filename }}" }, { stepName: "Cleanup" } ) - .test() + .test({ fields: { filename: "testfile.txt" } }) expect(result.steps[1].outputs.stdout).toEqual("INITIAL CONTENT\n") expect(result.steps[1].outputs.success).toEqual(true) @@ -65,6 +65,7 @@ describe("Execute Bash Automations", () => { it("should integrate bash output with row operations", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .queryRows( { tableId: table._id!, @@ -82,7 +83,7 @@ describe("Execute Bash Automations", () => { { text: "{{ steps.[Process Row Data].stdout }}" }, { stepName: "Log Result" } ) - .run() + .test({ fields: {} }) expect(result.steps[1].outputs.stdout).toContain( "Row data: test row - test description" @@ -94,7 +95,7 @@ describe("Execute Bash Automations", () => { it("should handle bash output in conditional logic", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { threshold: "5" } }) + .onAppAction() .bash( { code: "echo $(( {{ trigger.fields.threshold }} + 5 ))" }, { stepName: "Calculate Value" } @@ -112,7 +113,7 @@ describe("Execute Bash Automations", () => { { text: "Value was {{ steps.[Check Value].value }}" }, { stepName: "Log Result" } ) - .test() + .test({ fields: { threshold: "5" } }) expect(result.steps[0].outputs.stdout).toEqual("10\n") expect(result.steps[1].outputs.value).toEqual("high") @@ -121,12 +122,13 @@ describe("Execute Bash Automations", () => { it("should handle null values gracefully", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .bash( // @ts-expect-error - testing null input { code: null }, { stepName: "Null Command" } ) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.stdout).toBe( "Budibase bash automation failed: Invalid inputs" diff --git a/packages/server/src/automations/tests/steps/createRow.spec.ts b/packages/server/src/automations/tests/steps/createRow.spec.ts index a4523e15ef..ec644c2b5e 100644 --- a/packages/server/src/automations/tests/steps/createRow.spec.ts +++ b/packages/server/src/automations/tests/steps/createRow.spec.ts @@ -41,14 +41,14 @@ describe("test the create row action", () => { it("should be able to run the action", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { status: "new" } }) + .onAppAction() .serverLog({ text: "Starting create row flow" }, { stepName: "StartLog" }) .createRow({ row }, { stepName: "CreateRow" }) .serverLog( { text: "Row created with ID: {{ stepsByName.CreateRow.row._id }}" }, { stepName: "CreationLog" } ) - .test() + .test({ fields: { status: "new" } }) expect(result.steps[1].outputs.success).toBeDefined() expect(result.steps[1].outputs.id).toBeDefined() @@ -67,7 +67,7 @@ describe("test the create row action", () => { it("should return an error (not throw) when bad info provided", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { status: "error" } }) + .onAppAction() .serverLog({ text: "Starting error test flow" }, { stepName: "StartLog" }) .createRow( { @@ -78,14 +78,14 @@ describe("test the create row action", () => { }, { stepName: "CreateRow" } ) - .test() + .test({ fields: { status: "error" } }) expect(result.steps[1].outputs.success).toEqual(false) }) it("should check invalid inputs return an error", async () => { const result = await createAutomationBuilder(config) - .onAppAction({ fields: { status: "invalid" } }) + .onAppAction() .serverLog({ text: "Testing invalid input" }, { stepName: "StartLog" }) .createRow({ row: {} }, { stepName: "CreateRow" }) .filter({ @@ -97,7 +97,7 @@ describe("test the create row action", () => { { text: "This log should not appear" }, { stepName: "SkippedLog" } ) - .test() + .test({ fields: { status: "invalid" } }) expect(result.steps[1].outputs.success).toEqual(false) expect(result.steps.length).toBeLessThan(4) @@ -123,7 +123,7 @@ describe("test the create row action", () => { attachmentRow.file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .onAppAction({ fields: { type: "attachment" } }) + .onAppAction() .serverLog( { text: "Processing attachment upload" }, { stepName: "StartLog" } @@ -140,7 +140,7 @@ describe("test the create row action", () => { }, { stepName: "UploadLog" } ) - .test() + .test({ fields: { type: "attachment" } }) expect(result.steps[1].outputs.success).toEqual(true) expect(result.steps[1].outputs.row.file_attachment[0]).toHaveProperty("key") @@ -174,7 +174,7 @@ describe("test the create row action", () => { attachmentRow.single_file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .onAppAction({ fields: { type: "single-attachment" } }) + .onAppAction() .serverLog( { text: "Processing single attachment" }, { stepName: "StartLog" } @@ -209,7 +209,7 @@ describe("test the create row action", () => { }, }, }) - .test() + .test({ fields: { type: "single-attachment" } }) expect(result.steps[1].outputs.success).toEqual(true) expect(result.steps[1].outputs.row.single_file_attachment).toHaveProperty( @@ -245,7 +245,7 @@ describe("test the create row action", () => { attachmentRow.single_file_attachment = attachmentObject const result = await createAutomationBuilder(config) - .onAppAction({ fields: { type: "invalid-attachment" } }) + .onAppAction() .serverLog( { text: "Testing invalid attachment keys" }, { stepName: "StartLog" } @@ -278,7 +278,7 @@ describe("test the create row action", () => { }, }, }) - .test() + .test({ fields: { type: "invalid-attachment" } }) expect(result.steps[1].outputs.success).toEqual(false) expect(result.steps[1].outputs.response).toEqual( diff --git a/packages/server/src/automations/tests/steps/delay.spec.ts b/packages/server/src/automations/tests/steps/delay.spec.ts index 9dc6470f5a..173beccbda 100644 --- a/packages/server/src/automations/tests/steps/delay.spec.ts +++ b/packages/server/src/automations/tests/steps/delay.spec.ts @@ -16,7 +16,10 @@ describe("test the delay logic", () => { const time = 100 const before = performance.now() - await createAutomationBuilder(config).delay({ time }).run() + await createAutomationBuilder(config) + .onAppAction() + .delay({ time }) + .test({ fields: {} }) const now = performance.now() diff --git a/packages/server/src/automations/tests/steps/deleteRow.spec.ts b/packages/server/src/automations/tests/steps/deleteRow.spec.ts index ee8c9b329f..8c141f82da 100644 --- a/packages/server/src/automations/tests/steps/deleteRow.spec.ts +++ b/packages/server/src/automations/tests/steps/deleteRow.spec.ts @@ -21,12 +21,13 @@ describe("test the delete row action", () => { it("should be able to run the delete row action", async () => { await createAutomationBuilder(config) + .onAppAction() .deleteRow({ tableId: table._id!, id: row._id!, revision: row._rev, }) - .run() + .test({ fields: {} }) await config.api.row.get(table._id!, row._id!, { status: 404, @@ -35,20 +36,22 @@ describe("test the delete row action", () => { it("should check invalid inputs return an error", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .deleteRow({ tableId: "", id: "", revision: "" }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(false) }) it("should return an error when table doesn't exist", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .deleteRow({ tableId: "invalid", id: "invalid", revision: "invalid", }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(false) }) diff --git a/packages/server/src/automations/tests/steps/discord.spec.ts b/packages/server/src/automations/tests/steps/discord.spec.ts index 361b3517f3..9618a0c994 100644 --- a/packages/server/src/automations/tests/steps/discord.spec.ts +++ b/packages/server/src/automations/tests/steps/discord.spec.ts @@ -20,12 +20,13 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .discord({ url: "http://www.example.com", username: "joe_bloggs", content: "Hello, world", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response.foo).toEqual("bar") expect(result.steps[0].outputs.success).toEqual(true) }) diff --git a/packages/server/src/automations/tests/steps/filter.spec.ts b/packages/server/src/automations/tests/steps/filter.spec.ts index 51af262aff..23c191b38d 100644 --- a/packages/server/src/automations/tests/steps/filter.spec.ts +++ b/packages/server/src/automations/tests/steps/filter.spec.ts @@ -43,8 +43,9 @@ describe("test the filter logic", () => { ] it.each(pass)("should pass %p %p %p", async (field, condition, value) => { const result = await createAutomationBuilder(config) + .onAppAction() .filter({ field, condition: stringToFilterCondition(condition), value }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.result).toEqual(true) expect(result.steps[0].outputs.success).toEqual(true) @@ -60,8 +61,9 @@ describe("test the filter logic", () => { ] it.each(fail)("should fail %p %p %p", async (field, condition, value) => { const result = await createAutomationBuilder(config) + .onAppAction() .filter({ field, condition: stringToFilterCondition(condition), value }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.result).toEqual(false) expect(result.steps[0].outputs.success).toEqual(true) diff --git a/packages/server/src/automations/tests/steps/loop.spec.ts b/packages/server/src/automations/tests/steps/loop.spec.ts index 5e4f3e8126..1f985bd504 100644 --- a/packages/server/src/automations/tests/steps/loop.spec.ts +++ b/packages/server/src/automations/tests/steps/loop.spec.ts @@ -73,17 +73,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation with a trigger, loop, and create row step", async () => { const results = await createAutomationBuilder(config) - .onRowSaved( - { tableId: table._id! }, - { - row: { - name: "Trigger Row", - description: "This row triggers the automation", - }, - id: "1234", - revision: "1", - } - ) + .onRowSaved({ tableId: table._id! }) .loop({ option: LoopStepType.ARRAY, binding: [1, 2, 3], @@ -95,7 +85,14 @@ describe("Attempt to run a basic loop automation", () => { tableId: table._id, }, }) - .test() + .test({ + row: { + name: "Trigger Row", + description: "This row triggers the automation", + }, + id: "1234", + revision: "1", + }) expect(results.trigger).toBeDefined() expect(results.steps).toHaveLength(1) @@ -116,17 +113,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation where a loop step is between two normal steps to ensure context correctness", async () => { const results = await createAutomationBuilder(config) - .onRowSaved( - { tableId: table._id! }, - { - row: { - name: "Trigger Row", - description: "This row triggers the automation", - }, - id: "1234", - revision: "1", - } - ) + .onRowSaved({ tableId: table._id! }) .queryRows({ tableId: table._id!, }) @@ -136,7 +123,14 @@ describe("Attempt to run a basic loop automation", () => { }) .serverLog({ text: "Message {{loop.currentItem}}" }) .serverLog({ text: "{{steps.1.rows.0._id}}" }) - .test() + .test({ + row: { + name: "Trigger Row", + description: "This row triggers the automation", + }, + id: "1234", + revision: "1", + }) results.steps[1].outputs.items.forEach( (output: ServerLogStepOutputs, index: number) => { @@ -152,12 +146,13 @@ describe("Attempt to run a basic loop automation", () => { it("if an incorrect type is passed to the loop it should return an error", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop({ option: LoopStepType.ARRAY, binding: "1, 2, 3", }) .serverLog({ text: "Message {{loop.currentItem}}" }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs).toEqual({ success: false, @@ -167,13 +162,14 @@ describe("Attempt to run a basic loop automation", () => { it("ensure the loop stops if the failure condition is reached", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], failure: "test2", }) .serverLog({ text: "Message {{loop.currentItem}}" }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs).toEqual( expect.objectContaining({ @@ -185,6 +181,7 @@ describe("Attempt to run a basic loop automation", () => { it("ensure the loop stops if the max iterations are reached", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -192,13 +189,14 @@ describe("Attempt to run a basic loop automation", () => { }) .serverLog({ text: "{{loop.currentItem}}" }) .serverLog({ text: "{{steps.1.iterations}}" }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.iterations).toBe(2) }) it("should run an automation with loop and max iterations to ensure context correctness further down the tree", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop({ option: LoopStepType.ARRAY, binding: ["test", "test2", "test3"], @@ -206,24 +204,14 @@ describe("Attempt to run a basic loop automation", () => { }) .serverLog({ text: "{{loop.currentItem}}" }) .serverLog({ text: "{{steps.1.iterations}}" }) - .run() + .test({ fields: {} }) expect(results.steps[1].outputs.message).toContain("- 2") }) it("should run an automation where a loop is successfully run twice", async () => { const results = await createAutomationBuilder(config) - .onRowSaved( - { tableId: table._id! }, - { - row: { - name: "Trigger Row", - description: "This row triggers the automation", - }, - id: "1234", - revision: "1", - } - ) + .onRowSaved({ tableId: table._id! }) .loop({ option: LoopStepType.ARRAY, binding: [1, 2, 3], @@ -240,7 +228,14 @@ describe("Attempt to run a basic loop automation", () => { binding: "Message 1,Message 2,Message 3", }) .serverLog({ text: "{{loop.currentItem}}" }) - .test() + .test({ + row: { + name: "Trigger Row", + description: "This row triggers the automation", + }, + id: "1234", + revision: "1", + }) expect(results.trigger).toBeDefined() expect(results.steps).toHaveLength(2) @@ -275,6 +270,7 @@ describe("Attempt to run a basic loop automation", () => { it("should run an automation where a loop is used twice to ensure context correctness further down the tree", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop({ option: LoopStepType.ARRAY, binding: [1, 2, 3], @@ -287,7 +283,7 @@ describe("Attempt to run a basic loop automation", () => { }) .serverLog({ text: "{{loop.currentItem}}" }) .serverLog({ text: "{{steps.3.iterations}}" }) - .run() + .test({ fields: {} }) // We want to ensure that bindings are corr expect(results.steps[1].outputs.message).toContain("- 3") @@ -296,6 +292,7 @@ describe("Attempt to run a basic loop automation", () => { it("should use automation names to loop with", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .loop( { option: LoopStepType.ARRAY, @@ -311,7 +308,7 @@ describe("Attempt to run a basic loop automation", () => { { text: "{{steps.FirstLoopLog.iterations}}" }, { stepName: "FirstLoopIterationLog" } ) - .run() + .test({ fields: {} }) expect(results.steps[1].outputs.message).toContain("- 3") }) @@ -347,6 +344,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder(config) + .onAppAction() .queryRows({ tableId: table._id!, }) @@ -366,7 +364,7 @@ describe("Attempt to run a basic loop automation", () => { .queryRows({ tableId: table._id!, }) - .run() + .test({ fields: {} }) const expectedRows = [ { name: "Updated Row 1", value: 1 }, @@ -426,6 +424,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder(config) + .onAppAction() .queryRows( { tableId: table._id!, @@ -448,7 +447,7 @@ describe("Attempt to run a basic loop automation", () => { .queryRows({ tableId: table._id!, }) - .run() + .test({ fields: {} }) const expectedRows = [ { name: "Updated Row 1", value: 1 }, @@ -508,6 +507,7 @@ describe("Attempt to run a basic loop automation", () => { await config.api.row.bulkImport(table._id!, { rows }) const results = await createAutomationBuilder(config) + .onAppAction() .queryRows({ tableId: table._id!, }) @@ -522,7 +522,7 @@ describe("Attempt to run a basic loop automation", () => { .queryRows({ tableId: table._id!, }) - .run() + .test({ fields: {} }) expect(results.steps).toHaveLength(3) diff --git a/packages/server/src/automations/tests/steps/make.spec.ts b/packages/server/src/automations/tests/steps/make.spec.ts index 2d118d943f..bbc0c3791a 100644 --- a/packages/server/src/automations/tests/steps/make.spec.ts +++ b/packages/server/src/automations/tests/steps/make.spec.ts @@ -20,11 +20,12 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action", async () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .make({ url: "http://www.example.com", body: null, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response.foo).toEqual("bar") expect(result.steps[0].outputs.success).toEqual(true) @@ -46,11 +47,12 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .make({ body: { value: JSON.stringify(payload) }, url: "http://www.example.com", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response.foo).toEqual("bar") expect(result.steps[0].outputs.success).toEqual(true) @@ -58,11 +60,12 @@ describe("test the outgoing webhook action", () => { it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .make({ body: { value: "{ invalid json }" }, url: "http://www.example.com", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.httpStatus).toEqual(400) expect(result.steps[0].outputs.response).toEqual("Invalid payload JSON") diff --git a/packages/server/src/automations/tests/steps/n8n.spec.ts b/packages/server/src/automations/tests/steps/n8n.spec.ts index d3efb1aaeb..4ee3123d98 100644 --- a/packages/server/src/automations/tests/steps/n8n.spec.ts +++ b/packages/server/src/automations/tests/steps/n8n.spec.ts @@ -21,12 +21,13 @@ describe("test the outgoing webhook action", () => { it("should be able to run the action and default to 'get'", async () => { nock("http://www.example.com/").get("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .n8n({ url: "http://www.example.com", body: { test: "IGNORE_ME" }, authorization: "", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response).toEqual({ foo: "bar" }) expect(result.steps[0].outputs.httpStatus).toEqual(200) @@ -39,26 +40,28 @@ describe("test the outgoing webhook action", () => { .reply(200) const result = await createAutomationBuilder(config) + .onAppAction() .n8n({ url: "http://www.example.com", body: { value: JSON.stringify({ name: "Adam", age: 9 }) }, method: HttpMethod.POST, authorization: "", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toEqual(true) }) it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .n8n({ url: "http://www.example.com", body: { value: "{ value1 1 }" }, method: HttpMethod.POST, authorization: "", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.httpStatus).toEqual(400) expect(result.steps[0].outputs.response).toEqual("Invalid payload JSON") @@ -71,13 +74,14 @@ describe("test the outgoing webhook action", () => { .reply(200) const result = await createAutomationBuilder(config) + .onAppAction() .n8n({ url: "http://www.example.com", method: HttpMethod.HEAD, body: { test: "IGNORE_ME" }, authorization: "", }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toEqual(true) }) diff --git a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts index b1d13c6917..85ccfb8eac 100644 --- a/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts +++ b/packages/server/src/automations/tests/steps/outgoingWebhook.spec.ts @@ -24,13 +24,14 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .outgoingWebhook({ requestMethod: RequestType.POST, url: "http://www.example.com", requestBody: JSON.stringify({ a: 1 }), headers: {}, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toEqual(true) expect(result.steps[0].outputs.httpStatus).toEqual(200) @@ -39,13 +40,14 @@ describe("test the outgoing webhook action", () => { it("should return an error if something goes wrong in fetch", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .outgoingWebhook({ requestMethod: RequestType.GET, url: "www.invalid.com", requestBody: "", headers: {}, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toEqual(false) }) }) diff --git a/packages/server/src/automations/tests/steps/serverLog.spec.ts b/packages/server/src/automations/tests/steps/serverLog.spec.ts index 44a9f068b1..82f097d0da 100644 --- a/packages/server/src/automations/tests/steps/serverLog.spec.ts +++ b/packages/server/src/automations/tests/steps/serverLog.spec.ts @@ -14,8 +14,9 @@ describe("test the server log action", () => { it("should be able to log the text", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .serverLog({ text: "Hello World" }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.message).toEqual( `App ${config.getAppId()} - Hello World` ) diff --git a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts index ef851bc047..8d4a29c2b6 100644 --- a/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts +++ b/packages/server/src/automations/tests/steps/triggerAutomationRun.spec.ts @@ -17,24 +17,27 @@ describe("Test triggering an automation from another automation", () => { }) it("should trigger an other server log automation", async () => { - const automation = await createAutomationBuilder(config) + const { automation } = await createAutomationBuilder(config) + .onAppAction() .serverLog({ text: "Hello World" }) .save() const result = await createAutomationBuilder(config) + .onAppAction() .triggerAutomationRun({ automation: { automationId: automation._id!, }, timeout: env.getDefaults().AUTOMATION_THREAD_TIMEOUT, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(true) }) it("should fail gracefully if the automation id is incorrect", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .triggerAutomationRun({ automation: { // @ts-expect-error - incorrect on purpose @@ -42,7 +45,7 @@ describe("Test triggering an automation from another automation", () => { }, timeout: env.getDefaults().AUTOMATION_THREAD_TIMEOUT, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toBe(false) }) diff --git a/packages/server/src/automations/tests/steps/updateRow.spec.ts b/packages/server/src/automations/tests/steps/updateRow.spec.ts index 32c7b90446..a2f1825099 100644 --- a/packages/server/src/automations/tests/steps/updateRow.spec.ts +++ b/packages/server/src/automations/tests/steps/updateRow.spec.ts @@ -31,6 +31,7 @@ describe("test the update row action", () => { it("should be able to run the update row action", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .updateRow({ rowId: row._id!, row: { @@ -40,7 +41,7 @@ describe("test the update row action", () => { }, meta: {}, }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(true) const updatedRow = await config.api.row.get( @@ -53,20 +54,22 @@ describe("test the update row action", () => { it("should check invalid inputs return an error", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .updateRow({ meta: {}, row: {}, rowId: "" }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(false) }) it("should return an error when table doesn't exist", async () => { const results = await createAutomationBuilder(config) + .onAppAction() .updateRow({ row: { _id: "invalid" }, rowId: "invalid", meta: {}, }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(false) }) @@ -104,6 +107,7 @@ describe("test the update row action", () => { }) const results = await createAutomationBuilder(config) + .onAppAction() .updateRow({ rowId: row._id!, row: { @@ -115,7 +119,7 @@ describe("test the update row action", () => { }, meta: {}, }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(true) @@ -157,6 +161,7 @@ describe("test the update row action", () => { }) const results = await createAutomationBuilder(config) + .onAppAction() .updateRow({ rowId: row._id!, row: { @@ -174,7 +179,7 @@ describe("test the update row action", () => { }, }, }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.success).toEqual(true) diff --git a/packages/server/src/automations/tests/steps/zapier.spec.ts b/packages/server/src/automations/tests/steps/zapier.spec.ts index e897083d18..e6b5417563 100644 --- a/packages/server/src/automations/tests/steps/zapier.spec.ts +++ b/packages/server/src/automations/tests/steps/zapier.spec.ts @@ -21,8 +21,9 @@ describe("test the outgoing webhook action", () => { nock("http://www.example.com/").post("/").reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .zapier({ url: "http://www.example.com", body: null }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response.foo).toEqual("bar") expect(result.steps[0].outputs.success).toEqual(true) @@ -44,11 +45,12 @@ describe("test the outgoing webhook action", () => { .reply(200, { foo: "bar" }) const result = await createAutomationBuilder(config) + .onAppAction() .zapier({ url: "http://www.example.com", body: { value: JSON.stringify(payload) }, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.response.foo).toEqual("bar") expect(result.steps[0].outputs.success).toEqual(true) @@ -56,11 +58,12 @@ describe("test the outgoing webhook action", () => { it("should return a 400 if the JSON payload string is malformed", async () => { const result = await createAutomationBuilder(config) + .onAppAction() .zapier({ url: "http://www.example.com", body: { value: "{ invalid json }" }, }) - .run() + .test({ fields: {} }) expect(result.steps[0].outputs.success).toEqual(false) expect(result.steps[0].outputs.response).toEqual("Invalid payload JSON") From 28e931bc643ce93c6b5210bf043e3e35304d0c59 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 6 Feb 2025 17:06:36 +0000 Subject: [PATCH 10/11] Fix types. --- packages/server/src/tests/utilities/structures.ts | 4 +--- packages/types/src/documents/app/automation/schema.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index f99d961ae6..fa91c7ca0f 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -217,10 +217,8 @@ export function basicAutomation(opts?: DeepPartial): Automation { icon: "test", description: "test", type: AutomationStepType.TRIGGER, + inputs: {}, id: "test", - inputs: { - fields: {}, - }, schema: { inputs: { properties: {}, diff --git a/packages/types/src/documents/app/automation/schema.ts b/packages/types/src/documents/app/automation/schema.ts index 66a6f508fe..820858b48c 100644 --- a/packages/types/src/documents/app/automation/schema.ts +++ b/packages/types/src/documents/app/automation/schema.ts @@ -331,7 +331,7 @@ export type AutomationTriggerDefinition = Omit< export type AutomationTriggerInputs = T extends AutomationTriggerStepId.APP - ? void + ? void | Record : T extends AutomationTriggerStepId.CRON ? CronTriggerInputs : T extends AutomationTriggerStepId.ROW_ACTION From a2003701622846774071fb0fe2fd83d36e092d00 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 7 Feb 2025 09:37:21 +0000 Subject: [PATCH 11/11] Updated executev2 tests --- .../automations/tests/executeScriptV2.spec.ts | 63 +++++++++---------- .../tests/utilities/AutomationTestBuilder.ts | 1 + .../src/tests/utilities/TestConfiguration.ts | 1 + 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/packages/server/src/automations/tests/executeScriptV2.spec.ts b/packages/server/src/automations/tests/executeScriptV2.spec.ts index 3091fd8292..539af3cc66 100644 --- a/packages/server/src/automations/tests/executeScriptV2.spec.ts +++ b/packages/server/src/automations/tests/executeScriptV2.spec.ts @@ -21,47 +21,44 @@ describe("Execute Script Automations", () => { afterAll(setup.afterAll) it("should execute a basic script and return the result", async () => { - const builder = createAutomationBuilder({ - name: "Basic Script Execution", - }) + config.name = "Basic Script Execution" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: {} }) + .onAppAction() .executeScriptV2({ code: encodeJS("return 2 + 2") }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.value).toEqual(4) }) it("should access bindings from previous steps", async () => { - const builder = createAutomationBuilder({ - name: "Access Bindings", - }) + config.name = "Access Bindings" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: { data: [1, 2, 3] } }) + .onAppAction() .executeScriptV2( { code: encodeJS(`return $("trigger.fields.data").map(x => x * 2)`), }, { stepId: "binding-script-step" } ) - .run() + .test({ fields: { data: [1, 2, 3] } }) expect(results.steps[0].outputs.value).toEqual([2, 4, 6]) }) it("should handle script execution errors gracefully", async () => { - const builder = createAutomationBuilder({ - name: "Handle Script Errors", - }) + config.name = "Handle Script Errors" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: {} }) + .onAppAction() .executeScriptV2({ code: encodeJS("return nonexistentVariable.map(x => x)"), }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.response).toContain( "ReferenceError: nonexistentVariable is not defined" @@ -70,12 +67,11 @@ describe("Execute Script Automations", () => { }) it("should handle conditional logic in scripts", async () => { - const builder = createAutomationBuilder({ - name: "Conditional Script Logic", - }) + config.name = "Conditional Script Logic" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: { value: 10 } }) + .onAppAction() .executeScriptV2({ code: encodeJS(` if ($("trigger.fields.value") > 5) { @@ -85,18 +81,17 @@ describe("Execute Script Automations", () => { } `), }) - .run() + .test({ fields: { value: 10 } }) expect(results.steps[0].outputs.value).toEqual("Value is greater than 5") }) it("should use multiple steps and validate script execution", async () => { - const builder = createAutomationBuilder({ - name: "Multi-Step Script Execution", - }) + config.name = "Multi-Step Script Execution" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: {} }) + .onAppAction() .serverLog( { text: "Starting multi-step automation" }, { stepId: "start-log-step" } @@ -117,7 +112,7 @@ describe("Execute Script Automations", () => { .serverLog({ text: `Final result is {{ steps.ScriptingStep1.value }}`, }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.message).toContain( "Starting multi-step automation" @@ -128,16 +123,15 @@ describe("Execute Script Automations", () => { }) it("should fail if the code has not been encoded as a handlebars template", async () => { - const builder = createAutomationBuilder({ - name: "Invalid Code Encoding", - }) + config.name = "Invalid Code Encoding" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: {} }) + .onAppAction() .executeScriptV2({ code: "return 2 + 2", }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.response.message).toEqual( "Expected code to be a {{ js }} template block" @@ -146,16 +140,15 @@ describe("Execute Script Automations", () => { }) it("does not process embedded handlebars templates", async () => { - const builder = createAutomationBuilder({ - name: "Embedded Handlebars", - }) + config.name = "Embedded Handlebars" + const builder = createAutomationBuilder(config) const results = await builder - .appAction({ fields: {} }) + .onAppAction() .executeScriptV2({ code: encodeJS(`return "{{ triggers.row.whatever }}"`), }) - .run() + .test({ fields: {} }) expect(results.steps[0].outputs.value).toEqual( "{{ triggers.row.whatever }}" diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index 835a177ea8..0d8f6f2231 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -97,6 +97,7 @@ class BranchStepBuilder { loop = this.step(AutomationActionStepId.LOOP) serverLog = this.step(AutomationActionStepId.SERVER_LOG) executeScript = this.step(AutomationActionStepId.EXECUTE_SCRIPT) + executeScriptV2 = this.step(AutomationActionStepId.EXECUTE_SCRIPT_V2) filter = this.step(AutomationActionStepId.FILTER) bash = this.step(AutomationActionStepId.EXECUTE_BASH) openai = this.step(AutomationActionStepId.OPENAI) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 1f464b2ea4..ce7c0bc3e4 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -98,6 +98,7 @@ export default class TestConfiguration { request?: supertest.SuperTest started: boolean appId?: string + name?: string allApps: App[] app?: App prodApp?: App