From 594551e1b550368609f1e998f356e2dd28b354f3 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 12 Sep 2024 14:05:32 +0100 Subject: [PATCH 1/7] tests for filter steps --- .../tests/scenarios/scenarios.spec.ts | 167 +++++++++++++++++- .../tests/utilities/AutomationTestBuilder.ts | 9 + 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index 40d6094525..62a1b9db8f 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -1,8 +1,9 @@ import * as automation from "../../index" import * as setup from "../utilities" -import { LoopStepType, FieldType } from "@budibase/types" +import { LoopStepType, FieldType, Table } from "@budibase/types" import { createAutomationBuilder } from "../utilities/AutomationTestBuilder" import { DatabaseName } from "../../../integrations/tests/utils" +import { FilterConditions } from "../../../automations/steps/filter" describe("Automation Scenarios", () => { let config = setup.getConfig() @@ -196,4 +197,168 @@ describe("Automation Scenarios", () => { ) }) }) + describe.only("Automations with filter", () => { + let table: Table + + beforeEach(async () => { + table = await config.createTable({ + name: "TestTable", + type: "table", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + value: { + name: "value", + type: FieldType.NUMBER, + constraints: { + presence: true, + }, + }, + }, + }) + }) + + it("should stop an automation if the condition is not met", async () => { + const builder = createAutomationBuilder({ + name: "Test Equal", + }) + + const results = await builder + .appAction({ fields: {} }) + .createRow({ + row: { + name: "Equal Test", + value: 10, + tableId: table._id, + }, + }) + .queryRows({ + tableId: table._id!, + }) + .filter({ + field: "{{ steps.2.rows.0.value }}", + condition: FilterConditions.EQUAL, + value: 20, + }) + .serverLog({ text: "Equal condition met" }) + .run() + + expect(results.steps[2].outputs.success).toBeTrue() + expect(results.steps[2].outputs.result).toBeFalse() + expect(results.steps[3]).toBeUndefined() + }) + + it("should continue the automation if the condition is met", async () => { + const builder = createAutomationBuilder({ + name: "Test Not Equal", + }) + + const results = await builder + .appAction({ fields: {} }) + .createRow({ + row: { + name: "Not Equal Test", + value: 10, + tableId: table._id, + }, + }) + .queryRows({ + tableId: table._id!, + }) + .filter({ + field: "{{ steps.2.rows.0.value }}", + condition: FilterConditions.NOT_EQUAL, + value: 20, + }) + .serverLog({ text: "Not Equal condition met" }) + .run() + + expect(results.steps[2].outputs.success).toBeTrue() + expect(results.steps[2].outputs.result).toBeTrue() + expect(results.steps[3].outputs.success).toBeTrue() + }) + + const testCases = [ + { + condition: FilterConditions.EQUAL, + value: 10, + rowValue: 10, + expectPass: true, + }, + { + condition: FilterConditions.NOT_EQUAL, + value: 10, + rowValue: 20, + expectPass: true, + }, + { + condition: FilterConditions.GREATER_THAN, + value: 10, + rowValue: 15, + expectPass: true, + }, + { + condition: FilterConditions.LESS_THAN, + value: 10, + rowValue: 5, + expectPass: true, + }, + { + condition: FilterConditions.GREATER_THAN, + value: 10, + rowValue: 5, + expectPass: false, + }, + { + condition: FilterConditions.LESS_THAN, + value: 10, + rowValue: 15, + expectPass: false, + }, + ] + + testCases.forEach(({ condition, value, rowValue, expectPass }) => { + it(`should ${ + expectPass ? "pass" : "fail" + } the filter when condition is "${condition}" and value is ${value}`, async () => { + const builder = createAutomationBuilder({ + name: `Test ${condition}`, + }) + + const results = await builder + .appAction({ fields: {} }) + .createRow({ + row: { + name: `${condition} Test`, + value: rowValue, + tableId: table._id, + }, + }) + .queryRows({ + tableId: table._id!, + }) + .filter({ + field: "{{ steps.2.rows.0.value }}", + condition, + value, + }) + .serverLog({ + text: `${condition} condition ${expectPass ? "passed" : "failed"}`, + }) + .run() + + expect(results.steps[2].outputs.result).toBe(expectPass) + if (expectPass) { + expect(results.steps[3].outputs.success).toBeTrue() + } else { + expect(results.steps[3]).toBeUndefined() + } + }) + }) + }) }) diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index f477efabe4..6c89bd1f60 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -33,6 +33,7 @@ import { BranchStepInputs, SearchFilters, Branch, + FilterStepInputs, } from "@budibase/types" import TestConfiguration from "../../../tests/utilities/TestConfiguration" import * as setup from "../utilities" @@ -161,6 +162,14 @@ class BaseStepBuilder { input ) } + + filter(input: FilterStepInputs): this { + return this.step( + AutomationActionStepId.FILTER, + BUILTIN_ACTION_DEFINITIONS.FILTER, + input + ) + } } class StepBuilder extends BaseStepBuilder { build(): AutomationStep[] { From 980615b37b5b83b109278a012093ee06e9bc0e19 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 24 Sep 2024 11:08:44 +0100 Subject: [PATCH 2/7] looping query rows --- .../tests/scenarios/looping.spec.ts | 146 ++++++++++++++++++ .../tests/scenarios/scenarios.spec.ts | 2 +- 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/packages/server/src/automations/tests/scenarios/looping.spec.ts b/packages/server/src/automations/tests/scenarios/looping.spec.ts index 9bc382a187..9f7ff04156 100644 --- a/packages/server/src/automations/tests/scenarios/looping.spec.ts +++ b/packages/server/src/automations/tests/scenarios/looping.spec.ts @@ -5,6 +5,7 @@ import { LoopStepType, CreateRowStepOutputs, ServerLogStepOutputs, + FieldType, } from "@budibase/types" import { createAutomationBuilder } from "../utilities/AutomationTestBuilder" @@ -242,4 +243,149 @@ describe("Loop automations", () => { expect(results.steps[1].outputs.message).toContain("- 3") expect(results.steps[3].outputs.message).toContain("- 3") }) + + it("should run an automation with a loop and update row step", async () => { + const table = await config.createTable({ + name: "TestTable", + type: "table", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + value: { + name: "value", + type: FieldType.NUMBER, + constraints: { + presence: true, + }, + }, + }, + }) + + const rows = [ + { name: "Row 1", value: 1, tableId: table._id }, + { name: "Row 2", value: 2, tableId: table._id }, + { name: "Row 3", value: 3, tableId: table._id }, + ] + + for (const row of rows) { + await config.createRow(row) + } + + const builder = createAutomationBuilder({ + name: "Test Loop and Update Row", + }) + + const results = await builder + .appAction({ fields: {} }) + .queryRows({ + tableId: table._id!, + }) + .loop({ + option: LoopStepType.ARRAY, + binding: "{{ steps.1.rows }}", + }) + .updateRow({ + rowId: "{{ loop.currentItem._id }}", + row: { + name: "Updated {{ loop.currentItem.name }}", + value: "{{ loop.currentItem.value }}", + tableId: table._id, + }, + meta: {}, + }) + .queryRows({ + tableId: table._id!, + }) + .run() + + const expectedRows = [ + { name: "Updated Row 1", value: 1 }, + { name: "Updated Row 2", value: 2 }, + { name: "Updated Row 3", value: 3 }, + ] + + expect(results.steps[1].outputs.items).toEqual( + expect.arrayContaining( + expectedRows.map(row => + expect.objectContaining({ + success: true, + row: expect.objectContaining(row), + }) + ) + ) + ) + + expect(results.steps[2].outputs.rows).toEqual( + expect.arrayContaining( + expectedRows.map(row => expect.objectContaining(row)) + ) + ) + + expect(results.steps[1].outputs.items).toHaveLength(expectedRows.length) + expect(results.steps[2].outputs.rows).toHaveLength(expectedRows.length) + }) + + it("should run an automation with a loop and delete row step", async () => { + const table = await config.createTable({ + name: "TestTable", + type: "table", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + value: { + name: "value", + type: FieldType.NUMBER, + constraints: { + presence: true, + }, + }, + }, + }) + + const rows = [ + { name: "Row 1", value: 1, tableId: table._id }, + { name: "Row 2", value: 2, tableId: table._id }, + { name: "Row 3", value: 3, tableId: table._id }, + ] + + for (const row of rows) { + await config.createRow(row) + } + + const builder = createAutomationBuilder({ + name: "Test Loop and Delete Row", + }) + + const results = await builder + .appAction({ fields: {} }) + .queryRows({ + tableId: table._id!, + }) + .loop({ + option: LoopStepType.ARRAY, + binding: "{{ steps.1.rows }}", + }) + .deleteRow({ + tableId: table._id!, + id: "{{ loop.currentItem._id }}", + }) + .queryRows({ + tableId: table._id!, + }) + .run() + + expect(results.steps).toHaveLength(3) + + expect(results.steps[2].outputs.rows).toHaveLength(0) + }) }) diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index 62a1b9db8f..12b8392ffc 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -197,7 +197,7 @@ describe("Automation Scenarios", () => { ) }) }) - describe.only("Automations with filter", () => { + describe("Automations with filter", () => { let table: Table beforeEach(async () => { From 0c6946af62fae3a1f17926fd7f4c2cbed72486c4 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 24 Sep 2024 15:01:05 +0100 Subject: [PATCH 3/7] more automation tests --- .../tests/scenarios/scenarios.spec.ts | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index b54b7c0e0e..4e9340992d 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -196,6 +196,91 @@ describe("Automation Scenarios", () => { ) }) }) + + it("should trigger an automation which creates and then updates a row", async () => { + const table = await config.createTable({ + name: "TestTable", + type: "table", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + value: { + name: "value", + type: FieldType.NUMBER, + constraints: { + presence: true, + }, + }, + }, + }) + + const builder = createAutomationBuilder({ + name: "Test Create and Update Row", + }) + + const results = await builder + .appAction({ fields: {} }) + .createRow( + { + row: { + name: "Initial Row", + value: 1, + tableId: table._id, + }, + }, + { stepName: "CreateRowStep" } + ) + .updateRow( + { + rowId: "{{ steps.CreateRowStep.row._id }}", + row: { + name: "Updated Row", + value: 2, + tableId: table._id, + }, + meta: {}, + }, + { stepName: "UpdateRowStep" } + ) + .queryRows( + { + tableId: table._id!, + }, + { stepName: "QueryRowsStep" } + ) + .run() + + expect(results.steps).toHaveLength(3) + + expect(results.steps[0].outputs).toMatchObject({ + success: true, + row: { + name: "Initial Row", + value: 1, + }, + }) + + expect(results.steps[1].outputs).toMatchObject({ + success: true, + row: { + name: "Updated Row", + value: 2, + }, + }) + + const expectedRows = [{ name: "Updated Row", value: 2 }] + + expect(results.steps[2].outputs.rows).toEqual( + expect.arrayContaining( + expectedRows.map(row => expect.objectContaining(row)) + ) + ) + }) }) describe("Name Based Automations", () => { From 4a55021844c163c82d98c087a4ddf1fa2dea33db Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 24 Sep 2024 16:15:07 +0100 Subject: [PATCH 4/7] refs --- packages/account-portal | 2 +- packages/pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/account-portal b/packages/account-portal index 7899d07904..26903524ff 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 7899d07904d89d48954dd500da7b5dec32b781dd +Subproject commit 26903524ffb6e8f2d99b667ce7a84f09ea238073 diff --git a/packages/pro b/packages/pro index ec1d2bda75..e2fe0f9cc8 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit ec1d2bda756f02c6b4efdee086e4c59b0c2a1b0c +Subproject commit e2fe0f9cc856b4ee1a97df96d623b2d87d4e8733 From c89cebbecebfe52319a4b5f6ef0266b247d05a58 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 25 Sep 2024 16:39:49 +0100 Subject: [PATCH 5/7] ref --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 26903524ff..558a32dfd1 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 26903524ffb6e8f2d99b667ce7a84f09ea238073 +Subproject commit 558a32dfd1f55bd894804a503e7e1090937df88c From 44702bbfb6edda26ffa9af56434e2c38eb558512 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 26 Sep 2024 10:22:16 +0100 Subject: [PATCH 6/7] pr comments --- .../tests/scenarios/looping.spec.ts | 8 +- .../tests/scenarios/scenarios.spec.ts | 79 ++++++++++--------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/packages/server/src/automations/tests/scenarios/looping.spec.ts b/packages/server/src/automations/tests/scenarios/looping.spec.ts index 05d82ad7ba..379927342a 100644 --- a/packages/server/src/automations/tests/scenarios/looping.spec.ts +++ b/packages/server/src/automations/tests/scenarios/looping.spec.ts @@ -299,9 +299,7 @@ describe("Loop automations", () => { { name: "Row 3", value: 3, tableId: table._id }, ] - for (const row of rows) { - await config.createRow(row) - } + await config.api.row.bulkImport(table._id!, { rows }) const builder = createAutomationBuilder({ name: "Test Loop and Update Row", @@ -385,9 +383,7 @@ describe("Loop automations", () => { { name: "Row 3", value: 3, tableId: table._id }, ] - for (const row of rows) { - await config.createRow(row) - } + await config.api.row.bulkImport(table._id!, { rows }) const builder = createAutomationBuilder({ name: "Test Loop and Delete Row", diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index 4e9340992d..e0e2ef1cc9 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -444,43 +444,48 @@ describe("Automation Scenarios", () => { }, ] - testCases.forEach(({ condition, value, rowValue, expectPass }) => { - it(`should ${ - expectPass ? "pass" : "fail" - } the filter when condition is "${condition}" and value is ${value}`, async () => { - const builder = createAutomationBuilder({ - name: `Test ${condition}`, + it.each(testCases)( + "should pass the filter when condition is %s", + async ({ condition, value, rowValue, expectPass }) => { + it(`should ${ + expectPass ? "pass" : "fail" + } the filter when condition is "${condition}" and value is ${value}`, async () => { + const builder = createAutomationBuilder({ + name: `Test ${condition}`, + }) + + const results = await builder + .appAction({ fields: {} }) + .createRow({ + row: { + name: `${condition} Test`, + value: rowValue, + tableId: table._id, + }, + }) + .queryRows({ + tableId: table._id!, + }) + .filter({ + field: "{{ steps.2.rows.0.value }}", + condition, + value, + }) + .serverLog({ + text: `${condition} condition ${ + expectPass ? "passed" : "failed" + }`, + }) + .run() + + expect(results.steps[2].outputs.result).toBe(expectPass) + if (expectPass) { + expect(results.steps[3].outputs.success).toBeTrue() + } else { + expect(results.steps[3]).toBeUndefined() + } }) - - const results = await builder - .appAction({ fields: {} }) - .createRow({ - row: { - name: `${condition} Test`, - value: rowValue, - tableId: table._id, - }, - }) - .queryRows({ - tableId: table._id!, - }) - .filter({ - field: "{{ steps.2.rows.0.value }}", - condition, - value, - }) - .serverLog({ - text: `${condition} condition ${expectPass ? "passed" : "failed"}`, - }) - .run() - - expect(results.steps[2].outputs.result).toBe(expectPass) - if (expectPass) { - expect(results.steps[3].outputs.success).toBeTrue() - } else { - expect(results.steps[3]).toBeUndefined() - } - }) - }) + } + ) }) }) From 4bc30a018739f8e00c071783334920d1caf54e2e Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 26 Sep 2024 11:50:03 +0100 Subject: [PATCH 7/7] pr comment --- .../tests/scenarios/scenarios.spec.ts | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts index e0e2ef1cc9..850a04a95a 100644 --- a/packages/server/src/automations/tests/scenarios/scenarios.spec.ts +++ b/packages/server/src/automations/tests/scenarios/scenarios.spec.ts @@ -445,46 +445,40 @@ describe("Automation Scenarios", () => { ] it.each(testCases)( - "should pass the filter when condition is %s", + "should pass the filter when condition is $condition", async ({ condition, value, rowValue, expectPass }) => { - it(`should ${ - expectPass ? "pass" : "fail" - } the filter when condition is "${condition}" and value is ${value}`, async () => { - const builder = createAutomationBuilder({ - name: `Test ${condition}`, - }) - - const results = await builder - .appAction({ fields: {} }) - .createRow({ - row: { - name: `${condition} Test`, - value: rowValue, - tableId: table._id, - }, - }) - .queryRows({ - tableId: table._id!, - }) - .filter({ - field: "{{ steps.2.rows.0.value }}", - condition, - value, - }) - .serverLog({ - text: `${condition} condition ${ - expectPass ? "passed" : "failed" - }`, - }) - .run() - - expect(results.steps[2].outputs.result).toBe(expectPass) - if (expectPass) { - expect(results.steps[3].outputs.success).toBeTrue() - } else { - expect(results.steps[3]).toBeUndefined() - } + const builder = createAutomationBuilder({ + name: `Test ${condition}`, }) + + const results = await builder + .appAction({ fields: {} }) + .createRow({ + row: { + name: `${condition} Test`, + value: rowValue, + tableId: table._id, + }, + }) + .queryRows({ + tableId: table._id!, + }) + .filter({ + field: "{{ steps.2.rows.0.value }}", + condition, + value, + }) + .serverLog({ + text: `${condition} condition ${expectPass ? "passed" : "failed"}`, + }) + .run() + + expect(results.steps[2].outputs.result).toBe(expectPass) + if (expectPass) { + expect(results.steps[3].outputs.success).toBeTrue() + } else { + expect(results.steps[3]).toBeUndefined() + } } ) })