budibase/packages/server/src/automations/tests/scenarios.spec.ts

498 lines
13 KiB
TypeScript

import * as automation from "../index"
import {
LoopStepType,
FieldType,
Table,
Datasource,
FilterCondition,
} from "@budibase/types"
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
import {
DatabaseName,
datasourceDescribe,
} from "../../integrations/tests/utils"
import { Knex } from "knex"
import { generator } from "@budibase/backend-core/tests"
import TestConfiguration from "../../tests/utilities/TestConfiguration"
import { basicTable } from "../../tests/utilities/structures"
describe("Automation Scenarios", () => {
const config = new TestConfiguration()
beforeEach(async () => {
await automation.init()
await config.init()
})
afterAll(() => {
config.end()
})
describe("Row Automations", () => {
it("should trigger an automation which then creates a row", async () => {
const table = await config.api.table.save(basicTable())
const results = await createAutomationBuilder(config)
.onRowUpdated({ tableId: table._id! })
.createRow({
row: {
name: "{{trigger.row.name}}",
description: "{{trigger.row.description}}",
tableId: table._id,
},
})
.test({
row: { name: "Test", description: "TEST" },
id: "1234",
})
expect(results.steps).toHaveLength(1)
expect(results.steps[0].outputs).toMatchObject({
success: true,
row: {
name: "Test",
description: "TEST",
},
})
})
it("should trigger an automation which queries the database", async () => {
const table = await config.api.table.save(basicTable())
const row = {
name: "Test Row",
description: "original description",
}
await config.api.row.save(table._id!, row)
await config.api.row.save(table._id!, row)
const results = await createAutomationBuilder(config)
.onAppAction()
.queryRows({
tableId: table._id!,
})
.test({ fields: {} })
expect(results.steps).toHaveLength(1)
expect(results.steps[0].outputs.rows).toHaveLength(2)
})
it("should trigger an automation which queries the database then deletes a row", async () => {
const table = await config.api.table.save(basicTable())
const row = {
name: "DFN",
description: "original description",
}
await config.api.row.save(table._id!, row)
await config.api.row.save(table._id!, row)
const results = await createAutomationBuilder(config)
.onAppAction()
.queryRows({
tableId: table._id!,
})
.deleteRow({
tableId: table._id!,
id: "{{ steps.1.rows.0._id }}",
})
.queryRows({
tableId: table._id!,
})
.test({ fields: {} })
expect(results.steps).toHaveLength(3)
expect(results.steps[1].outputs.success).toBeTruthy()
expect(results.steps[2].outputs.rows).toHaveLength(1)
})
it("should trigger an automation which creates and then updates a row", async () => {
const table = await config.api.table.save({
...basicTable(),
name: "TestTable",
type: "table",
schema: {
name: {
name: "name",
type: FieldType.STRING,
constraints: {
presence: true,
},
},
value: {
name: "value",
type: FieldType.NUMBER,
constraints: {
presence: true,
},
},
},
})
const results = await createAutomationBuilder(config)
.onAppAction()
.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" }
)
.test({ fields: {} })
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", () => {
it("should fetch and delete a rpw using automation naming", async () => {
const table = await config.api.table.save(basicTable())
const row = {
name: "DFN",
description: "original description",
}
await config.api.row.save(table._id!, row)
await config.api.row.save(table._id!, row)
const results = await createAutomationBuilder(config)
.onAppAction()
.queryRows(
{
tableId: table._id!,
},
{ stepName: "InitialQueryStep" }
)
.deleteRow({
tableId: table._id!,
id: "{{ steps.InitialQueryStep.rows.0._id }}",
})
.queryRows({
tableId: table._id!,
})
.test({ fields: {} })
expect(results.steps).toHaveLength(3)
expect(results.steps[1].outputs.success).toBeTruthy()
expect(results.steps[2].outputs.rows).toHaveLength(1)
})
})
describe("Automations with filter", () => {
let table: Table
beforeEach(async () => {
table = await config.api.table.save({
...basicTable(),
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 results = await createAutomationBuilder(config)
.onAppAction()
.createRow({
row: {
name: "Equal Test",
value: 10,
tableId: table._id,
},
})
.queryRows({
tableId: table._id!,
})
.filter({
field: "{{ steps.2.rows.0.value }}",
condition: FilterCondition.EQUAL,
value: 20,
})
.serverLog({ text: "Equal condition met" })
.test({ fields: {} })
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 results = await createAutomationBuilder(config)
.onAppAction()
.createRow({
row: {
name: "Not Equal Test",
value: 10,
tableId: table._id,
},
})
.queryRows({
tableId: table._id!,
})
.filter({
field: "{{ steps.2.rows.0.value }}",
condition: FilterCondition.NOT_EQUAL,
value: 20,
})
.serverLog({ text: "Not Equal condition met" })
.test({ fields: {} })
expect(results.steps[2].outputs.success).toBeTrue()
expect(results.steps[2].outputs.result).toBeTrue()
expect(results.steps[3].outputs.success).toBeTrue()
})
const testCases = [
{
condition: FilterCondition.EQUAL,
value: 10,
rowValue: 10,
expectPass: true,
},
{
condition: FilterCondition.NOT_EQUAL,
value: 10,
rowValue: 20,
expectPass: true,
},
{
condition: FilterCondition.GREATER_THAN,
value: 10,
rowValue: 15,
expectPass: true,
},
{
condition: FilterCondition.LESS_THAN,
value: 10,
rowValue: 5,
expectPass: true,
},
{
condition: FilterCondition.GREATER_THAN,
value: 10,
rowValue: 5,
expectPass: false,
},
{
condition: FilterCondition.LESS_THAN,
value: 10,
rowValue: 15,
expectPass: false,
},
]
it.each(testCases)(
"should pass the filter when condition is $condition",
async ({ condition, value, rowValue, expectPass }) => {
const results = await createAutomationBuilder(config)
.onAppAction()
.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"}`,
})
.test({ fields: {} })
expect(results.steps[2].outputs.result).toBe(expectPass)
if (expectPass) {
expect(results.steps[3].outputs.success).toBeTrue()
} else {
expect(results.steps[3]).toBeUndefined()
}
}
)
})
it("Check user is passed through from row trigger", async () => {
const table = await config.api.table.save(basicTable())
const results = await createAutomationBuilder(config)
.onRowUpdated({ tableId: table._id! })
.serverLog({ text: "{{ [user].[email] }}" })
.test({
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)
.onAppAction()
.serverLog({ text: "{{ [user].[email] }}" })
.test({ fields: {} })
expect(results.steps[0].outputs.message).toContain("example.com")
})
})
const descriptions = datasourceDescribe({ only: [DatabaseName.MYSQL] })
if (descriptions.length) {
describe.each(descriptions)("/rows ($dbName)", ({ config, dsProvider }) => {
let datasource: Datasource
let client: Knex
beforeAll(async () => {
const ds = await dsProvider()
datasource = ds.datasource!
client = ds.client!
})
it("should query an external database for some data then insert than into an internal table", async () => {
const newTable = await config.api.table.save({
...basicTable(),
name: "table",
type: "table",
schema: {
name: {
name: "name",
type: FieldType.STRING,
constraints: {
presence: true,
},
},
age: {
name: "age",
type: FieldType.NUMBER,
constraints: {
presence: true,
},
},
},
})
const tableName = generator.guid()
await client.schema.createTable(tableName, table => {
table.string("name")
table.integer("age")
})
const rows = [
{ name: "Joe", age: 20 },
{ name: "Bob", age: 25 },
{ name: "Paul", age: 30 },
]
await client(tableName).insert(rows)
const query = await config.api.query.save({
name: "test query",
datasourceId: datasource._id!,
parameters: [],
fields: {
sql: client(tableName).select("*").toSQL().toNative().sql,
},
transformer: "",
schema: {},
readable: true,
queryVerb: "read",
})
const results = await createAutomationBuilder(config)
.onAppAction()
.executeQuery({
query: {
queryId: query._id!,
},
})
.loop({
option: LoopStepType.ARRAY,
binding: "{{ steps.1.response }}",
})
.createRow({
row: {
name: "{{ loop.currentItem.name }}",
age: "{{ loop.currentItem.age }}",
tableId: newTable._id!,
},
})
.queryRows({
tableId: newTable._id!,
})
.test({ fields: {} })
expect(results.steps).toHaveLength(3)
expect(results.steps[1].outputs.iterations).toBe(3)
expect(results.steps[1].outputs.items).toHaveLength(3)
expect(results.steps[2].outputs.rows).toHaveLength(3)
rows.forEach(expectedRow => {
expect(results.steps[2].outputs.rows).toEqual(
expect.arrayContaining([expect.objectContaining(expectedRow)])
)
})
})
})
}