diff --git a/.eslintignore b/.eslintignore index 4acae1f7b..84d08bb1b 100755 --- a/.eslintignore +++ b/.eslintignore @@ -27,4 +27,6 @@ packages/core/cli/templates/plugin/src/client/*.tpl packages/app/client/src/.plugins docker storage -benchmark \ No newline at end of file +benchmark +packages/core/cli/src +t.js \ No newline at end of file diff --git a/.github/workflows/nocobase-test-e2e.yml b/.github/workflows/nocobase-test-e2e.yml index 70f973049..19785e810 100644 --- a/.github/workflows/nocobase-test-e2e.yml +++ b/.github/workflows/nocobase-test-e2e.yml @@ -63,11 +63,9 @@ jobs: __E2E__: true - run: npx playwright install chromium --with-deps - name: Test with postgres - run: yarn e2e test -x --skip-reporter + run: yarn e2e p-test --stop-on-error env: __E2E__: true - APP_BASE_URL: http://127.0.0.1:13000 - APP_PORT: 13000 APP_ENV: production LOGGER_LEVEL: error DB_DIALECT: postgres @@ -76,4 +74,4 @@ jobs: DB_USER: nocobase DB_PASSWORD: password DB_DATABASE: nocobase - timeout-minutes: 90 + timeout-minutes: 180 diff --git a/packages/core/cli/package.json b/packages/core/cli/package.json index 9aa425f2b..c01a8efb8 100644 --- a/packages/core/cli/package.json +++ b/packages/core/cli/package.json @@ -19,6 +19,7 @@ "fs-extra": "^11.1.1", "pm2": "^5.2.0", "portfinder": "^1.0.28", + "p-all": "3.0.0", "serve": "^13.0.2", "tsx": "^4.6.2" }, diff --git a/packages/core/cli/src/commands/e2e.js b/packages/core/cli/src/commands/e2e.js index 845a978c5..00b4e6823 100644 --- a/packages/core/cli/src/commands/e2e.js +++ b/packages/core/cli/src/commands/e2e.js @@ -2,6 +2,7 @@ const { Command } = require('commander'); const { run, isPortReachable } = require('../util'); const { execSync } = require('node:child_process'); const axios = require('axios'); +const { pTest } = require('./p-test'); /** * 检查服务是否启动成功 @@ -200,4 +201,12 @@ module.exports = (cli) => { e2e.command('install-deps').action(async () => { await run('npx', ['playwright', 'install', '--with-deps']); }); + + e2e + .command('p-test') + .option('--stop-on-error') + .option('--concurrency [concurrency]', '', 3) + .action(async (opts) => { + await pTest(opts); + }); }; diff --git a/packages/core/cli/src/commands/p-test.js b/packages/core/cli/src/commands/p-test.js new file mode 100644 index 000000000..99b7cf0b5 --- /dev/null +++ b/packages/core/cli/src/commands/p-test.js @@ -0,0 +1,68 @@ +const execa = require('execa'); +const { resolve, dirname } = require('path'); +const pAll = require('p-all'); +const dotenv = require('dotenv'); +const fs = require('fs'); +const { Client } = require('pg'); +const glob = require('glob'); + +let ENV_FILE = resolve(process.cwd(), '.env.e2e'); + +if (!fs.existsSync(ENV_FILE)) { + ENV_FILE = resolve(process.cwd(), '.env.e2e.example'); +} + +const data = fs.readFileSync(ENV_FILE, 'utf-8'); +const config = { + ...dotenv.parse(data), + ...process.env, +}; + +async function runApp(index = 1, dir) { + const database = `nocobase${index}`; + const client = new Client({ + host: config['DB_HOST'], + port: Number(config['DB_PORT']), + user: config['DB_USER'], + password: config['DB_PASSWORD'], + database: 'postgres', + }); + await client.connect(); + await client.query(`DROP DATABASE IF EXISTS "${database}"`); + await client.query(`CREATE DATABASE "${database}";`); + await client.end(); + return execa('yarn', ['nocobase', 'e2e', 'test', dir, '-x', '--skip-reporter'], { + shell: true, + stdio: 'inherit', + env: { + ...config, + CI: true, + __E2E__: true, + APP_BASE_URL: undefined, + LOGGER_LEVEL: 'error', + APP_ENV: 'production', + APP_PORT: 20000 + index, + DB_DATABASE: `nocobase${index}`, + SOCKET_PATH: `storage/gateway-e2e-${index}.sock`, + PM2_HOME: resolve(process.cwd(), `storage/.pm2-${index}`), + PLAYWRIGHT_AUTH_FILE: resolve(process.cwd(), `storage/playwright/.auth/admin-${index}.json`), + }, + }); +} + +exports.pTest = async (options) => { + const files = glob.sync('packages/**/__e2e__/**/*.test.ts', { + root: process.cwd(), + }); + const fileSet = new Set(); + + for (const file of files) { + fileSet.add(dirname(file)); + } + + const commands = [...fileSet.values()].map((v, i) => { + return () => runApp(i + 1, v); + }); + + await pAll(commands, { concurrency: 3, stopOnError: false, ...options }); +}; diff --git a/packages/core/cli/src/util.js b/packages/core/cli/src/util.js index 8c4d51fab..1e7bbeaf1 100644 --- a/packages/core/cli/src/util.js +++ b/packages/core/cli/src/util.js @@ -217,6 +217,9 @@ exports.genTsConfigPaths = function genTsConfigPaths() { paths[`${packageJsonName}/server`] = [`${relativePath}/src/server`]; paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`]; } + if (packageJsonName === '@nocobase/plugin-workflow-test') { + paths[`${packageJsonName}/e2e`] = [`${relativePath}/src/e2e`]; + } }); const tsConfigJsonPath = join(cwd, './tsconfig.paths.json'); @@ -272,7 +275,7 @@ exports.initEnv = function initEnv() { } } - if (process.argv[2] === 'e2e') { + if (!process.env.APP_ENV_PATH && process.argv[2] === 'e2e') { // 用于存放 playwright 自动生成的相关的文件 generatePlaywrightPath(); if (!fs.existsSync('.env.e2e') && fs.existsSync('.env.e2e.example')) { diff --git a/packages/core/client/src/modules/form-creation/__e2e__/dragAndDrop.test.ts b/packages/core/client/src/modules/form-creation/__e2e__/dragAndDrop.test.ts index 9820c7fb4..55bb92243 100644 --- a/packages/core/client/src/modules/form-creation/__e2e__/dragAndDrop.test.ts +++ b/packages/core/client/src/modules/form-creation/__e2e__/dragAndDrop.test.ts @@ -1,6 +1,8 @@ import { expect, oneFormBlockBasedOnUsers, test } from '@nocobase/test/e2e'; test('fields', async ({ page, mockPage }) => { + // 在 CI 中多进程跑的时候,可能需要等待更长的时间 + test.slow(); await mockPage(oneFormBlockBasedOnUsers).goto(); await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click(); await page.getByRole('menuitem', { name: 'Nickname' }).click(); diff --git a/packages/core/client/src/modules/table/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/table/__e2e__/schemaSettings.test.ts index 74d978002..3a000241d 100644 --- a/packages/core/client/src/modules/table/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/table/__e2e__/schemaSettings.test.ts @@ -117,7 +117,7 @@ test.describe('table block schema settings', () => { .dragTo(page.getByLabel('table-index-1').getByRole('img', { name: 'menu' })); // 等待表格刷新 - await page.waitForTimeout(1000); + await page.waitForTimeout(2000); email1 = await page.getByText(records[0].email).boundingBox(); email2 = await page.getByText(records[1].email).boundingBox(); diff --git a/packages/core/test/src/e2e/e2eUtils.ts b/packages/core/test/src/e2e/e2eUtils.ts index e369d895f..d9b507688 100644 --- a/packages/core/test/src/e2e/e2eUtils.ts +++ b/packages/core/test/src/e2e/e2eUtils.ts @@ -253,7 +253,7 @@ export class NocoPage { const _test = base.extend({ mockPage: async ({ page }, use) => { // 保证每个测试运行时 faker 的随机值都是一样的 - faker.seed(1); + // faker.seed(1); const nocoPages: NocoPage[] = []; const mockPage = (config?: PageConfig) => { @@ -284,27 +284,27 @@ const _test = base.extend({ collectionSettings = omitSomeFields( Array.isArray(collectionSettings) ? collectionSettings : [collectionSettings], ); - collectionsName = collectionSettings.map((item) => item.name); + collectionsName = [...collectionsName, ...collectionSettings.map((item) => item.name)]; await createCollections(collectionSettings); }; await use(_createCollections); if (collectionsName.length) { - await deleteCollections(collectionsName); + await deleteCollections(_.uniq(collectionsName)); } }, mockCollections: async ({ page }, use) => { let collectionsName = []; const destroy = async () => { if (collectionsName.length) { - await deleteCollections(collectionsName); + await deleteCollections(_.uniq(collectionsName)); } }; const mockCollections = async (collectionSettings: CollectionSetting[]) => { collectionSettings = omitSomeFields(collectionSettings); - collectionsName = collectionSettings.map((item) => item.name); + collectionsName = [...collectionsName, ...collectionSettings.map((item) => item.name)]; return createCollections(collectionSettings); }; @@ -315,13 +315,13 @@ const _test = base.extend({ let collectionsName = []; const destroy = async () => { if (collectionsName.length) { - await deleteCollections(collectionsName); + await deleteCollections(_.uniq(collectionsName)); } }; const mockCollection = async (collectionSetting: CollectionSetting, options?: { manualDestroy: boolean }) => { const collectionSettings = omitSomeFields([collectionSetting]); - collectionsName = collectionSettings.map((item) => item.name); + collectionsName = [...collectionsName, ...collectionSettings.map((item) => item.name)]; return createCollections(collectionSettings); }; diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfAssociatedCollection.test.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfAssociatedCollection.test.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts new file mode 100644 index 000000000..8e6d0cb55 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts @@ -0,0 +1,519 @@ +import { faker } from '@faker-js/faker'; +import { + AggregateNode, + CollectionTriggerNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, aggregated data table, no filtering, normal table integer fields not de-emphasised COUNT', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const aggregateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建聚合节点数据表 + const aggregateNodeCollectionDisplayName = `自动>组织[普通表]${aggregateNodeAppendText}`; + const aggregateNodeCollectionName = `tt_amt_org${aggregateNodeAppendText}`; + const aggregateNodeFieldName = 'staffnum'; + const aggregateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), aggregateNodeAppendText) + .collections, + ); + const aggregateNodeCollectionData = [ + { staffnum: 3, regcapital: 3.12 }, + { staffnum: 3, regcapital: 3.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 5, regcapital: 5.6 }, + ]; + const aggregateNodeCollectionRecords = await mockRecords(aggregateNodeCollectionName, aggregateNodeCollectionData); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置聚合数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'aggregate', exact: true }).click(); + const aggregateRecordNodeName = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Aggregate-Aggregate', { exact: true }).getByRole('textbox').fill(aggregateRecordNodeName); + const aggregateRecordNode = new AggregateNode(page, aggregateRecordNodeName); + const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText(); + await aggregateRecordNode.nodeConfigure.click(); + await aggregateRecordNode.collectionDropDown.click(); + await page.getByText(aggregateNodeCollectionDisplayName).click(); + await aggregateRecordNode.aggregatedFieldDropDown.click(); + await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click(); + await aggregateRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === aggregateRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + expect(queryRecordNodeJobResult).toBe(aggregateNodeCollectionData.length); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, aggregated data table, no filtering, normal table integer fields not de-emphasised SUM', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const aggregateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建聚合节点数据表 + const aggregateNodeCollectionDisplayName = `自动>组织[普通表]${aggregateNodeAppendText}`; + const aggregateNodeCollectionName = `tt_amt_org${aggregateNodeAppendText}`; + const aggregateNodeFieldName = 'staffnum'; + const aggregateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), aggregateNodeAppendText) + .collections, + ); + const aggregateNodeCollectionData = [ + { staffnum: 3, regcapital: 3.12 }, + { staffnum: 3, regcapital: 3.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 5, regcapital: 5.6 }, + ]; + const aggregateNodeCollectionRecords = await mockRecords(aggregateNodeCollectionName, aggregateNodeCollectionData); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置聚合数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'aggregate', exact: true }).click(); + const aggregateRecordNodeName = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Aggregate-Aggregate', { exact: true }).getByRole('textbox').fill(aggregateRecordNodeName); + const aggregateRecordNode = new AggregateNode(page, aggregateRecordNodeName); + const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText(); + await aggregateRecordNode.nodeConfigure.click(); + await aggregateRecordNode.sumRadio.click(); + await aggregateRecordNode.collectionDropDown.click(); + await page.getByText(aggregateNodeCollectionDisplayName).click(); + await aggregateRecordNode.aggregatedFieldDropDown.click(); + await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click(); + await aggregateRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === aggregateRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + // aggregateNodeCollectionData中staffnum字段值总和 + const aggregateNodeCollectionDataSum = aggregateNodeCollectionData.reduce((total, currentValue) => { + return total + currentValue.staffnum; + }, 0); + expect(queryRecordNodeJobResult).toBe(aggregateNodeCollectionDataSum); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, aggregated data table, no filtering, normal table integer fields not de-emphasised AVG', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const aggregateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建聚合节点数据表 + const aggregateNodeCollectionDisplayName = `自动>组织[普通表]${aggregateNodeAppendText}`; + const aggregateNodeCollectionName = `tt_amt_org${aggregateNodeAppendText}`; + const aggregateNodeFieldName = 'staffnum'; + const aggregateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), aggregateNodeAppendText) + .collections, + ); + const aggregateNodeCollectionData = [ + { staffnum: 3, regcapital: 3.12 }, + { staffnum: 3, regcapital: 3.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 5, regcapital: 5.6 }, + ]; + const aggregateNodeCollectionRecords = await mockRecords(aggregateNodeCollectionName, aggregateNodeCollectionData); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置聚合数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'aggregate', exact: true }).click(); + const aggregateRecordNodeName = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Aggregate-Aggregate', { exact: true }).getByRole('textbox').fill(aggregateRecordNodeName); + const aggregateRecordNode = new AggregateNode(page, aggregateRecordNodeName); + const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText(); + await aggregateRecordNode.nodeConfigure.click(); + await aggregateRecordNode.avgRadio.click(); + await aggregateRecordNode.collectionDropDown.click(); + await page.getByText(aggregateNodeCollectionDisplayName).click(); + await aggregateRecordNode.aggregatedFieldDropDown.click(); + await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click(); + await aggregateRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === aggregateRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + // aggregateNodeCollectionData中staffnum字段值平均值 + const aggregateNodeCollectionDataAvg = + aggregateNodeCollectionData.reduce((total, currentValue) => { + return total + currentValue.staffnum; + }, 0) / aggregateNodeCollectionData.length; + expect(queryRecordNodeJobResult).toBe(aggregateNodeCollectionDataAvg); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, aggregated data table, no filtering, normal table integer fields not de-emphasised MIN', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const aggregateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建聚合节点数据表 + const aggregateNodeCollectionDisplayName = `自动>组织[普通表]${aggregateNodeAppendText}`; + const aggregateNodeCollectionName = `tt_amt_org${aggregateNodeAppendText}`; + const aggregateNodeFieldName = 'staffnum'; + const aggregateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), aggregateNodeAppendText) + .collections, + ); + const aggregateNodeCollectionData = [ + { staffnum: 3, regcapital: 3.12 }, + { staffnum: 3, regcapital: 3.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 5, regcapital: 5.6 }, + ]; + const aggregateNodeCollectionRecords = await mockRecords(aggregateNodeCollectionName, aggregateNodeCollectionData); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置聚合数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'aggregate', exact: true }).click(); + const aggregateRecordNodeName = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Aggregate-Aggregate', { exact: true }).getByRole('textbox').fill(aggregateRecordNodeName); + const aggregateRecordNode = new AggregateNode(page, aggregateRecordNodeName); + const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText(); + await aggregateRecordNode.nodeConfigure.click(); + await aggregateRecordNode.minRadio.click(); + await aggregateRecordNode.collectionDropDown.click(); + await page.getByText(aggregateNodeCollectionDisplayName).click(); + await aggregateRecordNode.aggregatedFieldDropDown.click(); + await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click(); + await aggregateRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === aggregateRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + // aggregateNodeCollectionData中staffnum字段值最小值 + const aggregateNodeCollectionDataMin = aggregateNodeCollectionData.reduce((min, currentValue) => { + return currentValue.staffnum < min ? currentValue.staffnum : min; + }, aggregateNodeCollectionData[0].staffnum); + expect(queryRecordNodeJobResult).toBe(aggregateNodeCollectionDataMin); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, aggregated data table, no filtering, normal table integer fields not de-emphasised MAX', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const aggregateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建聚合节点数据表 + const aggregateNodeCollectionDisplayName = `自动>组织[普通表]${aggregateNodeAppendText}`; + const aggregateNodeCollectionName = `tt_amt_org${aggregateNodeAppendText}`; + const aggregateNodeFieldName = 'staffnum'; + const aggregateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), aggregateNodeAppendText) + .collections, + ); + const aggregateNodeCollectionData = [ + { staffnum: 3, regcapital: 3.12 }, + { staffnum: 3, regcapital: 3.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 4, regcapital: 4.6 }, + { staffnum: 5, regcapital: 5.6 }, + ]; + const aggregateNodeCollectionRecords = await mockRecords(aggregateNodeCollectionName, aggregateNodeCollectionData); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置聚合数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'aggregate', exact: true }).click(); + const aggregateRecordNodeName = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Aggregate-Aggregate', { exact: true }).getByRole('textbox').fill(aggregateRecordNodeName); + const aggregateRecordNode = new AggregateNode(page, aggregateRecordNodeName); + const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText(); + await aggregateRecordNode.nodeConfigure.click(); + await aggregateRecordNode.maxRadio.click(); + await aggregateRecordNode.collectionDropDown.click(); + await page.getByText(aggregateNodeCollectionDisplayName).click(); + await aggregateRecordNode.aggregatedFieldDropDown.click(); + await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click(); + await aggregateRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === aggregateRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + // aggregateNodeCollectionData中staffnum字段值最大值 + const aggregateNodeCollectionDataMax = aggregateNodeCollectionData.reduce((max, currentValue) => { + return currentValue.staffnum > max ? currentValue.staffnum : max; + }, aggregateNodeCollectionData[0].staffnum); + expect(queryRecordNodeJobResult).toBe(aggregateNodeCollectionDataMax); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/configuration.test.ts b/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/configuration.test.ts new file mode 100644 index 000000000..8ea0ae543 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/configuration.test.ts @@ -0,0 +1,659 @@ +import { faker } from '@faker-js/faker'; +import { + CreateWorkFlow, + EditWorkFlow, + FormEventTriggerNode, + WorkflowListRecords, + apiCreateRecordTriggerFormEvent, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test.describe('Configuration page to configure the Trigger node', () => { + test('Form Submit Button Binding Workflow Add Data Trigger', async ({ + page, + mockPage, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const formEventTriggerNode = new FormEventTriggerNode(page, workFlowName, triggerNodeCollectionName); + await formEventTriggerNode.nodeConfigure.click(); + await formEventTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await formEventTriggerNode.submitButton.click(); + + //配置录入数据区块 + const newPage = mockPage(); + await newPage.goto(); + await page.waitForLoadState('networkidle'); + await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover(); + await page.getByRole('menuitem', { name: 'table Table' }).hover(); + await page.getByRole('menuitem', { name: `${triggerNodeCollectionDisplayName}` }).click(); + + // 移开鼠标,关闭菜单 + await page.mouse.move(300, 0); + + await page.getByText('Configure columns').hover(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await page.getByText('Configure actions').hover(); + await page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch').click(); + await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeEnabled(); + await page.getByLabel(`action-Action-Add new-create-${triggerNodeCollectionName}-table`).click(); + await page.getByLabel(`schema-initializer-Grid-CreateFormBlockInitializers-${triggerNodeCollectionName}`).hover(); + await page.getByRole('menuitem', { name: 'form Form' }).click(); + // 移开鼠标,关闭菜单 + await page.mouse.move(300, 0); + await page + .getByLabel(`schema-initializer-ActionBar-CreateFormActionInitializers-${triggerNodeCollectionName}`) + .hover(); + await page.getByRole('menuitem', { name: 'Submit' }).click(); + // 绑定工作流 + await page.getByLabel(`schema-initializer-Grid-FormItemInitializers-${triggerNodeCollectionName}`).hover(); + await page.getByRole('menuitem', { name: `${triggerNodeFieldDisplayName}` }).click(); + await page.mouse.move(300, 0); + await page.getByLabel(`action-Action-Submit-submit-${triggerNodeCollectionName}-form`).hover(); + await page + .getByRole('button', { name: `designer-schema-settings-Action-Action.Designer-${triggerNodeCollectionName}` }) + .hover(); + await page.getByRole('menuitem', { name: 'Bind workflows' }).click(); + await page.getByRole('button', { name: 'plus Add workflow' }).click(); + await page.getByRole('button', { name: 'Select workflow' }).click(); + await page.getByRole('option', { name: workFlowName }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + + // 2、测试步骤:进入“数据区块”-“添加”按钮,填写表单,点击“确定”按钮 + const fieldData = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByRole('textbox').fill(fieldData); + await page.getByLabel(`action-Action-Submit-submit-${triggerNodeCollectionName}-form`, { exact: true }).click(); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(1000); + // 3、预期结果:触发次数为1 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Form Submit to Workflow Button Add Data Trigger', async ({ page, mockPage, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const formEventTriggerNode = new FormEventTriggerNode(page, workFlowName, triggerNodeCollectionName); + await formEventTriggerNode.nodeConfigure.click(); + await formEventTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await formEventTriggerNode.submitButton.click(); + + //配置录入数据区块 + const newPage = mockPage(); + await newPage.goto(); + await page.waitForLoadState('networkidle'); + await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover(); + await page.getByRole('menuitem', { name: 'table Table' }).hover(); + await page.getByRole('menuitem', { name: `${triggerNodeCollectionDisplayName}` }).click(); + + // 移开鼠标,关闭菜单 + await page.mouse.move(300, 0); + + await page.getByText('Configure columns').hover(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await page.getByText('Configure actions').hover(); + await page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch').click(); + await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeEnabled(); + + await page.getByLabel(`action-Action-Add new-create-${triggerNodeCollectionName}-table`).click(); + await page.getByLabel(`schema-initializer-Grid-CreateFormBlockInitializers-${triggerNodeCollectionName}`).hover(); + await page.getByRole('menuitem', { name: 'form Form' }).click(); + + // 移开鼠标,关闭菜单 + await page.mouse.move(300, 0); + + // 绑定工作流 + await page + .getByLabel(`schema-initializer-ActionBar-CreateFormActionInitializers-${triggerNodeCollectionName}`) + .hover(); + await page.getByRole('menuitem', { name: 'Customize' }).hover(); + await page.getByRole('menuitem', { name: 'Submit to workflow' }).click(); + await page.getByLabel(`schema-initializer-Grid-FormItemInitializers-${triggerNodeCollectionName}`).hover(); + await page.getByRole('menuitem', { name: `${triggerNodeFieldDisplayName}` }).click(); + await page.mouse.move(300, 0); + await page.getByRole('button', { name: 'Submit to workflow' }).hover(); + await page + .getByRole('button', { name: `designer-schema-settings-Action-Action.Designer-${triggerNodeCollectionName}` }) + .hover(); + await page.getByRole('menuitem', { name: 'Bind workflows' }).click(); + await page.getByRole('button', { name: 'plus Add workflow' }).click(); + await page.getByRole('button', { name: 'Select workflow' }).click(); + await page.getByRole('option', { name: workFlowName }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + + // 2、测试步骤:进入“数据区块”-“添加”按钮,填写表单,点击“确定”按钮 + const fieldData = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByRole('textbox').fill(fieldData); + await page + .getByLabel(`action-Action-Submit to workflow-customize:triggerWorkflows-${triggerNodeCollectionName}-form`) + .click(); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(1000); + // 3、预期结果:触发次数为1 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration Page Path Jump Workflow Management Page', () => { + test('Form event Workflow Configuration Page Path Jump Workflow Management Page', async ({ + page, + mockPage, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + // const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + // const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + // const triggerNodeFieldName = 'orgname'; + // const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + // await mockCollections(appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText).collections); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + + // 2、测试步骤:跳转到工作流管理页面 + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Form event Workflow History Version Configuration Page Path Jump Workflow Management Page', async ({ + page, + mockPage, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByLabel('more').click(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + //元素重复 + await page.getByLabel('version', { exact: true }).click(); + await page.getByLabel('version-1').click(); + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Form event Workflow Execution Log Page Path Jump Workflow Management Page', async ({ + page, + mockPage, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + await page.getByRole('link', { name: 'Workflow', exact: true }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test.skip('Form event Workflow Execution Log Page Path Jump Execution Log Screen', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + //跳转其他执行日志界面,元素无法定位 + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + // expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page version switching', () => {}); + +test.describe('Configuration page disable enable', () => { + test('Form event Workflow Add Data Trigger Disable Do Not Trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: false, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + // 3、预期结果:触发次数为1 + let getWorkflow = await apiGetWorkflow(workflowId); + let getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + let getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(0); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByRole('switch', { name: 'On Off' }).click(); + + await apiCreateRecordTriggerFormEvent(triggerNodeCollectionName, triggerWorkflows, { + orgname: triggerNodeCollectionRecordOne + '1', + }); + await page.waitForTimeout(1000); + + getWorkflow = await apiGetWorkflow(workflowId); + getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Form event Workflow Add Data Trigger Disable Enable Post Trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + // 3、预期结果:触发次数为1 + let getWorkflow = await apiGetWorkflow(workflowId); + let getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + let getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByRole('switch', { name: 'On Off' }).click(); + + await apiCreateRecordTriggerFormEvent(triggerNodeCollectionName, triggerWorkflows, { + orgname: triggerNodeCollectionRecordOne + '1', + }); + await page.waitForTimeout(1000); + + getWorkflow = await apiGetWorkflow(workflowId); + getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page execution history', () => {}); + +test.describe('Configuration page copy to new version', () => { + test('Copy the Form event of the Configuration Trigger node', async ({ page, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + const WorkflowKey = workflowObj.key; + + //配置工作流触发器 + const triggerNodeData = { config: { collection: triggerNodeCollectionName, appends: [] } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = + triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerWorkflows = WorkflowKey; + const triggerNodeCollectionRecords = await apiCreateRecordTriggerFormEvent( + triggerNodeCollectionName, + triggerWorkflows, + { orgname: triggerNodeCollectionRecordOne }, + ); + await page.waitForTimeout(1000); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByLabel('more').click(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + + // 3、预期结果:新版本工作流配置内容同旧版本一样 + const formEventTriggerNode = new FormEventTriggerNode(page, workFlowName, triggerNodeCollectionName); + await formEventTriggerNode.nodeConfigure.click(); + await expect(page.getByRole('button', { name: triggerNodeCollectionDisplayName })).toBeVisible(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page delete version', () => {}); + +test.describe('Node Add Modify Delete', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/workflowCRUD.test.ts b/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/workflowCRUD.test.ts new file mode 100644 index 000000000..2898575ca --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-form-trigger/src/client/__e2e__/workflowCRUD.test.ts @@ -0,0 +1,175 @@ +import { faker } from '@faker-js/faker'; +import { + CreateWorkFlow, + EditWorkFlow, + FormEventTriggerNode, + WorkflowListRecords, + apiCreateRecordTriggerFormEvent, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test.describe('Filter', () => { + test('filter workflow name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、筛选工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Add new', () => { + test('add new Form event', async ({ page }) => { + // 添加工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Action-Add new-workflows').click(); + const createWorkFlow = new CreateWorkFlow(page); + const workFlowName = faker.string.alphanumeric(5); + await createWorkFlow.name.fill(workFlowName); + await createWorkFlow.triggerType.click(); + await page.getByRole('option', { name: 'Form event' }).click(); + await page.getByLabel('action-Action-Submit-workflows').click(); + + // 3、预期结果:列表中出现新建的工作流 + await expect(page.getByText(workFlowName)).toBeVisible(); + + // 4、后置处理:删除工作流 + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(workFlowName)).toBeHidden(); + }); +}); + +test.describe('Sync', () => {}); + +test.describe('Delete', () => { + test('delete Form event', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 删除工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeHidden(); + + // 4、后置处理:删除工作流 + }); +}); + +test.describe('Edit', () => { + test('edit Form event name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + let workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 编辑工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`).click(); + const editWorkFlow = new EditWorkFlow(page, workFlowName); + workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + await editWorkFlow.name.fill(workFlowName); + await page.getByLabel('action-Action-Submit-workflows').click(); + await page.waitForLoadState('networkidle'); + // 3、预期结果:编辑成功,列表中出现编辑后的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Duplicate', () => { + test('Duplicate Form event triggers with only unconfigured trigger nodes', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'form', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、复制工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Duplicate-workflows-${workFlowName}`).click(); + await page.getByLabel(`action-Action-Submit-workflows-${workFlowName}`).click(); + await page.waitForLoadState('networkidle'); + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(`${workFlowName} copy`)).toBeAttached(); + + // 4、后置处理:删除工作流 + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName} copy`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(`${workFlowName} copy`)).toBeHidden(); + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Executed', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow-test/client.d.ts b/packages/plugins/@nocobase/plugin-workflow-test/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-workflow-test/client.js b/packages/plugins/@nocobase/plugin-workflow-test/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-workflow-test/e2e.d.ts b/packages/plugins/@nocobase/plugin-workflow-test/e2e.d.ts new file mode 100644 index 000000000..55bd41fbf --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/e2e.d.ts @@ -0,0 +1,2 @@ +export * from './dist/e2e'; +export { default } from './dist/e2e'; diff --git a/packages/plugins/@nocobase/plugin-workflow-test/e2e.js b/packages/plugins/@nocobase/plugin-workflow-test/e2e.js new file mode 100644 index 000000000..3ad440cd1 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/e2e.js @@ -0,0 +1 @@ +module.exports = require('./dist/e2e/index.js'); diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/client/index.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/client/index.ts index e69de29bb..30c93b8ab 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/client/index.ts @@ -0,0 +1,3 @@ +import { Plugin } from '@nocobase/client'; + +export default class extends Plugin {} diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts new file mode 100644 index 000000000..687b64c8d --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eCollectionModel.ts @@ -0,0 +1,4250 @@ +import { dayjs } from '@nocobase/utils'; +import { lowerCase } from 'lodash'; + +/** + * 为json对象中collections节点的name、title属性添加后缀 + * 为json对象中fields节点的collectionName、through、target性添加后缀 + * @param jsonObj json对象 + * @param appendText 后缀文本 + */ +export function appendJsonCollectionName(jsonObj: any, appendText: string) { + appendCollectionsNode(jsonObj, appendText); + for (const item of jsonObj['collections'][0]['fields']) { + appendFieldsNode(item, appendText); + } + return jsonObj; +} + +function appendCollectionsNode(jsonObj: any, appendText: string) { + jsonObj['collections'][0]['name'] += appendText; + jsonObj['collections'][0]['title'] += appendText; +} + +function appendFieldsNode(jsonObj: any, appendText: string) { + for (const key in jsonObj) { + if (key === 'collectionName' || key === 'through' || key === 'target') { + jsonObj[key] += appendText; + } + } +} + +/** + * builtin_ 为NocoBase内置模板,同NocoBase在页面创建效果一致主要用法3种: + * 1是测试用例仅需一个无业务字段的表即可验证, + * 2是作为多对多的中间表, + * 3是存在先表后关系字段的情况,使用create内置模板表再update表的方式更新数据表来添加全部字段 + * e2e_ 按照不同场景的数据表,可以多个测试用例共用一个或一套数据表 + */ +export const builtinGeneral = { + collections: [ + { + key: 'hqh4qy21m46', + name: 'tt_amt_org', + title: '自动>组织[普通表]', + inherit: false, + hidden: false, + description: null, + fields: [ + { + key: 'z3fvals50xp', + name: 'id', + type: 'bigInt', + interface: 'id', + description: null, + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: '3cp1zy4ogle', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + description: null, + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: '6l38n4nt7nb', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + description: null, + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: '3xds0p7ersr', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + description: null, + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'slqyx974mzc', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + description: null, + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + ], + category: [], + logging: true, + autoGenId: true, + createdBy: true, + updatedBy: true, + createdAt: true, + updatedAt: true, + sortable: true, + template: 'general', + view: false, + schema: 'public', + }, + ], +}; + +export const builtinCalendar = { + collections: [ + { + key: '7lfgblnyr0w', + name: 'tt_amt_org_cld', + title: '自动>组织[日历表]', + inherit: false, + hidden: false, + description: null, + fields: [ + { + key: 'nq6a40fz0nd', + name: 'cron', + type: 'string', + interface: 'select', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + title: '{{t("Repeats")}}', + 'x-component': 'CronSet', + 'x-component-props': 'allowClear', + enum: [ + { + label: '{{t("Daily")}}', + value: '0 0 0 * * ?', + }, + { + label: '{{t("Weekly")}}', + value: 'every_week', + }, + { + label: '{{t("Monthly")}}', + value: 'every_month', + }, + { + label: '{{t("Yearly")}}', + value: 'every_year', + }, + ], + }, + }, + { + key: 'qtxt638o4nq', + name: 'exclude', + type: 'json', + interface: null, + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + }, + { + key: 'ly88an4fg8f', + name: 'id', + type: 'bigInt', + interface: 'id', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: '8fiqtpovg79', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'hup33jdnp9j', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: '4ky6wvfhduq', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: '1xht59blk26', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + description: null, + collectionName: 'tt_amt_org_cld', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + ], + category: [], + logging: true, + template: 'calendar', + view: false, + createdBy: true, + updatedBy: true, + createdAt: true, + updatedAt: true, + sortable: true, + schema: 'public', + }, + ], +}; + +export const builtinTree = { + collections: [ + { + key: 'wvgsufr1uxv', + name: 'tt_amt_org_tree', + title: '自动>组织[树表]', + inherit: false, + hidden: false, + description: null, + fields: [ + { + key: '4ihc87rzn00', + name: 'parentId', + type: 'bigInt', + interface: 'integer', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + isForeignKey: true, + uiSchema: { + type: 'number', + title: '{{t("Parent ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + target: 'tt_amt_org_tree', + }, + { + key: 'j58bfihyjdy', + name: 'parent', + type: 'belongsTo', + interface: 'm2o', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + foreignKey: 'parentId', + treeParent: true, + onDelete: 'CASCADE', + uiSchema: { + title: '{{t("Parent")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + }, + target: 'tt_amt_org_tree', + targetKey: 'id', + }, + { + key: '6r6b98g0hcz', + name: 'children', + type: 'hasMany', + interface: 'o2m', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + foreignKey: 'parentId', + treeChildren: true, + onDelete: 'CASCADE', + uiSchema: { + title: '{{t("Children")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: true, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + }, + target: 'tt_amt_org_tree', + targetKey: 'id', + sourceKey: 'id', + }, + { + key: 'hju1ggqps4n', + name: 'id', + type: 'bigInt', + interface: 'id', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: '1y7rpmh8muj', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'ieknujjdkv9', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: 'cv35rzoqna6', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'z21wfpry7uh', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + description: null, + collectionName: 'tt_amt_org_tree', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + ], + category: [], + logging: true, + autoGenId: true, + createdBy: true, + updatedBy: true, + createdAt: true, + updatedAt: true, + sortable: true, + template: 'tree', + view: false, + tree: 'adjacencyList', + schema: 'public', + }, + ], +}; + +export const builtinFile = { + collections: [ + { + key: '6hzed4cvpxo', + name: 'tt_amt_file', + title: '自动>档案[文件表]', + inherit: false, + hidden: false, + description: null, + fields: [ + { + key: 'qo0w8dcl45u', + name: 'title', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("Title")}}', + 'x-component': 'Input', + }, + }, + { + key: 'ayzc9b3aee5', + name: 'filename', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("File name", { ns: "file-manager" })}}', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + { + key: 'ftn60zv1rfr', + name: 'extname', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("Extension name", { ns: "file-manager" })}}', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + { + key: '59oj6m0r8be', + name: 'size', + type: 'integer', + interface: 'integer', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'number', + title: '{{t("Size", { ns: "file-manager" })}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + 'x-component-props': { + stringMode: true, + step: '0', + }, + }, + }, + { + key: 'burt1tfbcjx', + name: 'mimetype', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("Mime type", { ns: "file-manager" })}}', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + { + key: '1zfhbqod0i1', + name: 'path', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("Path")}}', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + { + key: 'b0bmsmrfqqw', + name: 'url', + type: 'string', + interface: 'input', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("URL")}}', + 'x-component': 'Input.URL', + 'x-read-pretty': true, + }, + }, + { + key: 'indgsp9sqqq', + name: 'preview', + type: 'string', + interface: 'url', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + field: 'url', + deletable: false, + uiSchema: { + type: 'string', + title: '{{t("Preview")}}', + 'x-component': 'Preview', + 'x-read-pretty': true, + }, + }, + { + key: '2p8d1h46x9l', + name: 'storage', + type: 'belongsTo', + interface: null, + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + comment: '存储引擎', + target: 'storages', + foreignKey: 'storageId', + deletable: false, + targetKey: 'id', + }, + { + key: 'im0yegvb5jt', + name: 'meta', + type: 'jsonb', + interface: null, + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + deletable: false, + defaultValue: {}, + }, + { + key: 'tjr9nr5thoh', + name: 'id', + type: 'bigInt', + interface: 'id', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: 's48vjiqbjcq', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'y3t7jii4m8w', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: 'z5gvejdbfyb', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'qgd8zko7b0z', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + description: null, + collectionName: 'tt_amt_file', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + ], + category: [], + logging: true, + template: 'file', + view: false, + createdBy: true, + updatedBy: true, + storage: 'local', + schema: 'public', + }, + ], +}; + +export const builtinExpression = { + collections: [ + { + key: 'w77iro3197g', + name: 'tt_amt_exp', + title: '自动>表达式[表达式表]', + inherit: false, + hidden: false, + description: null, + fields: [ + { + key: 'slc2i7yz2ck', + name: 'engine', + type: 'string', + interface: 'radioGroup', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + title: '{{t("Calculation engine")}}', + 'x-component': 'Radio.Group', + enum: [ + { + value: 'math.js', + label: 'Math.js', + tooltip: + "{{t('Math.js comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types')}}", + link: 'https://mathjs.org/', + }, + { + value: 'formula.js', + label: 'Formula.js', + tooltip: '{{t("Formula.js supports most Microsoft Excel formula functions.")}}', + link: 'https://formulajs.info/functions/', + }, + ], + default: 'formula.js', + }, + }, + { + key: 'mlvkdcg2yvq', + name: 'sourceCollection', + type: 'string', + interface: 'select', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + title: '{{t("Collection")}}', + 'x-component': 'CollectionSelect', + 'x-component-props': {}, + }, + }, + { + key: 'vdzigakt9ne', + name: 'expression', + type: 'text', + interface: 'expression', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + title: '{{t("Expression")}}', + 'x-component': 'DynamicExpression', + }, + }, + { + key: 'fx72pqt0fjv', + name: 'id', + type: 'bigInt', + interface: 'id', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: 'owiio66cw1u', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: '9fu8kmc69dj', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: 'uie8q16z2jj', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: '6jzyuc1jzro', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + description: null, + collectionName: 'tt_amt_exp', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + ], + category: [], + logging: true, + template: 'expression', + view: false, + createdBy: true, + updatedBy: true, + createdAt: true, + updatedAt: true, + sortable: true, + schema: 'public', + }, + ], +}; + +//普通表单表场景,无关系字段情况 +export const generalWithNoRelationalFields = { + collections: [ + { + key: '86lpobjxamt', + name: 'tt_amt_org', + title: '自动>组织[普通表]', + inherit: false, + hidden: false, + fields: [ + { + key: 'ohxa8xp9amt', + name: 'id', + type: 'bigInt', + interface: 'id', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + key: 'qjit9pcvamt', + name: 'createdAt', + type: 'date', + interface: 'createdAt', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'vijs3wcnamt', + name: 'createdBy', + type: 'belongsTo', + interface: 'createdBy', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: 'zsebjtwiamt', + name: 'updatedAt', + type: 'date', + interface: 'updatedAt', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + field: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }, + { + key: 'fa4c74phamt', + name: 'updatedBy', + type: 'belongsTo', + interface: 'updatedBy', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + target: 'users', + foreignKey: 'updatedById', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'AssociationField', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + targetKey: 'id', + }, + { + key: 'mv6h7f8lamt', + name: 'orgcode', + type: 'sequence', + interface: 'sequence', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + patterns: [ + { + type: 'string', + options: { + value: '区域编码', + }, + }, + { + type: 'integer', + options: { + digits: 9, + start: 1, + key: 57450, + }, + }, + ], + uiSchema: { + type: 'string', + 'x-component': 'Input', + 'x-component-props': {}, + title: '公司编号(自动编码)', + }, + inputable: false, + }, + { + key: '44u5zoyyamt', + name: 'orgname', + type: 'string', + interface: 'input', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: '公司名称(单行文本)', + }, + }, + { + key: 'tdq82bb0amt', + name: 'address', + type: 'text', + interface: 'textarea', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'Input.TextArea', + title: '公司地址(多行文本)', + }, + }, + { + key: '10xi6i24amt', + name: 'phone', + type: 'string', + interface: 'phone', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'Input', + 'x-component-props': { + type: 'tel', + }, + title: '负责人电话(手机号码)', + }, + }, + { + key: 'n3ykqhe4amt', + name: 'email', + type: 'string', + interface: 'email', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'Input', + 'x-validator': 'email', + title: '电子邮箱(电子邮箱)', + }, + }, + { + key: '5ddcgbpqamt', + name: 'staffnum', + type: 'bigInt', + interface: 'integer', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'number', + 'x-component': 'InputNumber', + 'x-component-props': { + stringMode: true, + step: '1', + }, + 'x-validator': 'integer', + title: '员工人数(整数)', + }, + }, + { + key: 'z59sf4ilamt', + name: 'insurednum', + type: 'bigInt', + interface: 'integer', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'number', + 'x-component': 'InputNumber', + 'x-component-props': { + stringMode: true, + step: '1', + }, + 'x-validator': 'integer', + title: '参保人数(整数)', + }, + }, + { + key: 'ecvb3a8xamt', + name: 'regcapital', + type: 'double', + interface: 'number', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + 'x-component-props': { + step: '0.0001', + stringMode: true, + }, + type: 'number', + 'x-component': 'InputNumber', + title: '注册资本(数字)', + }, + }, + { + key: 'ierg1fbvamt', + name: 'paidcapital', + type: 'double', + interface: 'number', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + 'x-component-props': { + step: '0.0001', + stringMode: true, + }, + type: 'number', + 'x-component': 'InputNumber', + title: '实缴资本(数字)', + }, + }, + { + key: '46kjnywaamt', + name: 'insuranceratio', + type: 'float', + interface: 'percent', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + type: 'string', + 'x-component': 'Percent', + title: '参保占比(百分比)', + }, + }, + { + key: '8j2vkys9amt', + name: 'isenable', + type: 'boolean', + interface: 'checkbox', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'boolean', + 'x-component': 'Checkbox', + title: '是否启用(勾选)', + }, + }, + { + key: '6f480de5amt', + name: 'status_singleselect', + type: 'string', + interface: 'select', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + enum: [ + { + value: '1', + label: '存续', + }, + { + value: '2', + label: '在业', + }, + { + value: '3', + label: '吊销', + }, + { + value: '4', + label: '注销', + }, + { + value: '5', + label: '迁入', + }, + { + value: '6', + label: '迁出', + }, + { + value: '7', + label: '停业', + }, + { + value: '8', + label: '清算', + }, + ], + type: 'string', + 'x-component': 'Select', + title: '公司状态(下拉单选)', + }, + }, + { + key: 'fcx8cw1ramt', + name: 'range_multipleselect', + type: 'array', + interface: 'multipleSelect', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + enum: [ + { + value: 'F3134', + label: '软件销售', + }, + { + value: 'I3006', + label: '软件开发', + }, + { + value: 'I3007', + label: '人工智能基础软件开发', + }, + { + value: 'I3008', + label: '人工智能应用软件开发', + }, + { + value: 'I3010', + label: '软件外包服务', + }, + { + value: 'I3011', + label: '网络与信息安全软件开发', + }, + { + value: 'I3012', + label: '人工智能理论与算法软件开发', + }, + { + value: 'I3014', + label: '数字文化创意软件开发', + }, + { + value: 'I3027', + label: '信息技术咨询服务', + }, + { + value: 'I3032', + label: '数据处理服务', + }, + { + value: 'I3034', + label: '计算机系统服务', + }, + { + value: 'L2032', + label: '信息咨询服务(不含许可类信息咨询服务)', + }, + { + value: 'L2095', + label: '企业管理咨询', + }, + { + value: 'M2070', + label: '技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广', + }, + { + value: 'O3010', + label: '咨询策划服务', + }, + { + value: 'P1029', + label: '业务培训(不含教育培训、职业技能培训等需取得许可的培训)', + }, + ], + type: 'array', + 'x-component': 'Select', + 'x-component-props': { + mode: 'multiple', + }, + title: '经营范围(下拉多选)', + }, + defaultValue: [], + }, + { + key: 'z71al7aaamt', + name: 'status_radio', + type: 'string', + interface: 'radioGroup', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + enum: [ + { + value: '1', + label: '存续', + }, + { + value: '2', + label: '在业', + }, + { + value: '3', + label: '吊销', + }, + { + value: '4', + label: '注销', + }, + { + value: '5', + label: '迁入', + }, + { + value: '6', + label: '迁出', + }, + { + value: '7', + label: '停业', + }, + { + value: '8', + label: '清算', + }, + ], + type: 'string', + 'x-component': 'Radio.Group', + title: '公司状态(单选)', + }, + }, + { + key: 'x8v9qf95amt', + name: 'range_check', + type: 'array', + interface: 'checkboxGroup', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + enum: [ + { + value: 'F3134', + label: '软件销售', + }, + { + value: 'I3006', + label: '软件开发', + }, + { + value: 'I3007', + label: '人工智能基础软件开发', + }, + { + value: 'I3008', + label: '人工智能应用软件开发', + }, + { + value: 'I3010', + label: '软件外包服务', + }, + { + value: 'I3011', + label: '网络与信息安全软件开发', + }, + { + value: 'I3012', + label: '人工智能理论与算法软件开发', + }, + { + value: 'I3014', + label: '数字文化创意软件开发', + }, + { + value: 'I3027', + label: '信息技术咨询服务', + }, + { + value: 'I3032', + label: '数据处理服务', + }, + { + value: 'I3034', + label: '计算机系统服务', + }, + { + value: 'L2032', + label: '信息咨询服务(不含许可类信息咨询服务)', + }, + { + value: 'L2095', + label: '企业管理咨询', + }, + { + value: 'M2070', + label: '技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广', + }, + { + value: 'O3010', + label: '咨询策划服务', + }, + { + value: 'P1029', + label: '业务培训(不含教育培训、职业技能培训等需取得许可的培训)', + }, + ], + type: 'string', + 'x-component': 'Checkbox.Group', + title: '经营范围(复选)', + }, + defaultValue: [], + }, + { + key: 'c50l1zguamt', + name: 'range_markdown', + type: 'text', + interface: 'markdown', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'Markdown', + title: '经营范围(Markdown)', + }, + }, + { + key: 'msj1qa4damt', + name: 'range_richtext', + type: 'text', + interface: 'richText', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + 'x-component': 'RichText', + title: '经营范围(富文本)', + }, + }, + { + key: 'q755hkxmamt', + name: 'establishdate', + type: 'date', + interface: 'datetime', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + 'x-component-props': { + dateFormat: 'YYYY-MM-DD', + gmt: false, + showTime: false, + }, + type: 'string', + 'x-component': 'DatePicker', + title: '成立日期(日期)', + }, + }, + { + key: '9kn4ujeramt', + name: 'range_json', + type: 'json', + interface: 'json', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + defaultValue: null, + uiSchema: { + type: 'object', + 'x-component': 'Input.JSON', + 'x-component-props': { + autoSize: { + minRows: 5, + }, + }, + default: null, + title: '经营范围(JSON)', + }, + }, + { + key: 'vqcsj7htamt', + name: 'url', + type: 'string', + interface: 'url', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'string', + title: '官网地址(URL)', + 'x-component': 'Input.URL', + }, + }, + { + key: '0naiw2nq4j0', + name: 'insuranceratio_formula', + type: 'formula', + interface: 'formula', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + dataType: 'double', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + }, + type: 'number', + 'x-component': 'Formula.Result', + 'x-read-pretty': true, + title: '参保占比(公式)', + }, + engine: 'math.js', + expression: '100*{{insurednum}}/{{staffnum}}', + }, + { + key: 'fd9sqp8usoj', + name: 'sharesnum', + type: 'bigInt', + interface: 'integer', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'number', + 'x-component': 'InputNumber', + 'x-component-props': { + stringMode: true, + step: '1', + }, + 'x-validator': 'integer', + title: '股票数量(整数)', + }, + }, + { + key: 'fd9sqp8uamt', + name: 'testdataid', + type: 'bigInt', + interface: 'integer', + collectionName: 'tt_amt_org', + parentKey: null, + reverseKey: null, + uiSchema: { + type: 'number', + 'x-component': 'InputNumber', + 'x-component-props': { + stringMode: true, + step: '1', + }, + 'x-validator': 'integer', + title: '测试数据id', + }, + }, + ], + category: [], + logging: true, + autoGenId: true, + createdBy: true, + updatedBy: true, + createdAt: true, + updatedAt: true, + sortable: true, + template: 'general', + }, + ], +}; + +export const generalWithNoRelationalFieldsData = [ + { + createdAt: '2023-07-16T21:30:58.000Z', + updatedAt: '2023-06-20T03:32:01.000Z', + createdById: 0, + updatedById: 3, + testdataid: 86, + orgcode: '350086', + orgname: 'Black Logistic LLC', + address: '闵行区宾川路314号', + phone: '21-040-5877', + email: 'cwlam@mail.com', + url: 'http://www.osara.co.jp/Food', + staffnum: 25, + regcapital: 85.8799, + paidcapital: 297.1762, + insuranceratio: 29.93, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3007', 'I3011', 'I3012', 'I3014', 'I3027'], + status_radio: '4', + range_check: ['I3010', 'I3011', 'I3014', 'I3027', 'L2095', 'M2070', 'O3010'], + establishdate: '2022-03-17T19:44:50.000Z', + }, + { + createdAt: '2023-07-13T13:20:10.000Z', + updatedAt: '2023-07-10T02:25:11.737Z', + createdById: 4, + updatedById: null, + testdataid: 41, + orgcode: '230041', + orgname: '孙記有限责任公司', + address: '770 Shennan E Rd, Cai Wu Wei, Luohu District', + phone: '755-4939-4147', + email: 'kars16@icloud.com', + url: 'http://www.lamhy.org/HouseholdKitchenAppliances', + staffnum: 67, + regcapital: 14.9121, + paidcapital: 215.5018, + insuranceratio: 42.26, + isenable: true, + status_singleselect: '7', + range_multipleselect: [ + 'F3134', + 'I3006', + 'I3007', + 'I3008', + 'I3012', + 'I3014', + 'I3027', + 'I3034', + 'L2095', + 'M2070', + 'P1029', + ], + status_radio: '1', + range_check: ['F3134', 'I3010', 'I3032', 'I3034', 'O3010', 'P1029'], + establishdate: '2022-08-01T01:51:06.000Z', + }, + { + createdAt: '2023-03-14T23:05:00.000Z', + updatedAt: '2023-07-10T02:25:11.729Z', + createdById: null, + updatedById: 4, + testdataid: 27, + orgcode: '340027', + orgname: 'Lisa Telecommunication Inc.', + address: '43 Zhongshan 5th Rd, Zimaling Shangquan', + phone: '760-7344-2889', + email: 'onnaku@gmail.com', + url: 'https://video.ziyihe404.net/CenturionGardenOutdoor', + staffnum: 104, + regcapital: 237.9354, + paidcapital: 131.6008, + insuranceratio: 4.22, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3032', 'I3034', 'L2032', 'M2070', 'O3010'], + status_radio: '6', + range_check: ['F3134', 'I3006', 'I3008', 'I3012', 'M2070', 'O3010'], + establishdate: '2021-02-19T00:02:48.000Z', + }, + { + createdAt: '2023-08-20T18:07:24.000Z', + updatedAt: '2023-07-10T02:25:11.741Z', + createdById: 5, + updatedById: null, + testdataid: 77, + orgcode: '450077', + orgname: '子异有限责任公司', + address: '619 4th Section Renmin South Road, Jinjiang District', + phone: '174-6088-1418', + email: 'taoshihan1@hotmail.com', + url: 'https://www.litc908.jp/ArtsHandicraftsSewing', + staffnum: 37, + regcapital: 237.5694, + paidcapital: 94.707, + insuranceratio: null, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3008', 'I3011', 'I3014'], + status_radio: '5', + range_check: ['I3006', 'I3007', 'I3008', 'I3010', 'I3034', 'L2032', 'L2095', 'M2070', 'O3010'], + establishdate: '2023-10-07T23:30:43.000Z', + }, + { + createdAt: '2023-07-11T07:34:49.000Z', + updatedAt: '2023-07-10T02:25:11.736Z', + createdById: 5, + updatedById: null, + testdataid: 25, + orgcode: '550025', + orgname: '赵記发展贸易有限责任公司', + address: '607 Sanlitun Road, Chaoyang District', + phone: '144-4751-3753', + email: null, + url: 'https://video.kakeungw.net/VideoGames', + staffnum: 64, + regcapital: 78.8595, + paidcapital: 71.565, + insuranceratio: 58.15, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3006', 'I3011', 'I3027', 'I3032', 'L2032', 'L2095'], + status_radio: '6', + range_check: ['I3008', 'I3027', 'I3034', 'L2032', 'O3010', 'P1029'], + establishdate: null, + }, + { + createdAt: '2023-04-28T09:13:50.000Z', + updatedAt: '2023-05-29T08:08:24.000Z', + createdById: 6, + updatedById: 5, + testdataid: 66, + orgcode: '440066', + orgname: '吴技术有限责任公司', + address: '793 Hongqiao Rd., Xu Hui District', + phone: '21-0094-0906', + email: 'yyut@icloud.com', + url: 'https://video.yeowtszhin.us/Appliances', + staffnum: 45, + regcapital: null, + paidcapital: 76.1281, + insuranceratio: 76.17, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3008', 'I3011', 'I3014', 'I3027', 'I3034', 'L2032', 'O3010'], + status_radio: '4', + range_check: ['F3134', 'I3011', 'I3014', 'I3027', 'I3032', 'I3034', 'L2095', 'O3010', 'P1029'], + establishdate: '2023-09-08T01:51:09.000Z', + }, + { + createdAt: '2023-03-20T17:13:24.000Z', + updatedAt: '2023-05-16T04:16:34.000Z', + createdById: 1, + updatedById: 6, + testdataid: 62, + orgcode: '510062', + orgname: 'Salazar Food LLC', + address: '716 Huaxia St, Jinghua Shangquan', + phone: '198-4847-7122', + email: 'auwingfat1219@gmail.com', + url: 'https://www.xielu.us/CellPhonesAccessories', + staffnum: 131, + regcapital: 273.1562, + paidcapital: 287.3437, + insuranceratio: 76.86, + isenable: true, + status_singleselect: '7', + range_multipleselect: [ + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3032', + 'L2032', + 'L2095', + 'O3010', + 'P1029', + ], + status_radio: '5', + range_check: [ + 'F3134', + 'I3006', + 'I3007', + 'I3011', + 'I3014', + 'I3027', + 'I3034', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + 'P1029', + ], + establishdate: '2021-12-14T18:38:12.000Z', + }, + { + createdAt: '2023-05-10T18:09:11.000Z', + updatedAt: '2023-08-13T01:37:53.000Z', + createdById: 9, + updatedById: 1, + testdataid: 20, + orgcode: '620020', + orgname: '岚有限责任公司', + address: '8 Tremont Road', + phone: '614-635-9277', + email: 'salazar1985@gmail.com', + url: 'https://www.gordemily.com/FilmSupplies', + staffnum: null, + regcapital: 219.7301, + paidcapital: 63.6256, + insuranceratio: 45.43, + isenable: null, + status_singleselect: '8', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3012', 'I3014', 'I3027', 'I3034', 'L2032', 'P1029'], + status_radio: '8', + range_check: ['F3134', 'I3006', 'I3011', 'I3014', 'I3027', 'I3032', 'L2095', 'M2070'], + establishdate: '2022-07-05T10:21:51.000Z', + }, + { + createdAt: '2023-05-05T06:12:02.000Z', + updatedAt: '2023-05-04T19:41:58.000Z', + createdById: 3, + updatedById: 6, + testdataid: 31, + orgcode: '430031', + orgname: 'Hughes LLC', + address: '980 4th Section Renmin South Road, Jinjiang District', + phone: '147-7853-0142', + email: 'scao@mail.com', + url: 'http://image.ikedayu1224.com/Baby', + staffnum: 135, + regcapital: 139.2834, + paidcapital: 173.014, + insuranceratio: 62.83, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3006', 'I3014', 'I3032', 'I3034', 'P1029'], + status_radio: '2', + range_check: ['F3134', 'I3006', 'I3008', 'I3010', 'I3034', 'M2070', 'O3010', 'P1029'], + establishdate: '2023-11-10T02:30:06.000Z', + }, + { + createdAt: '2023-05-03T05:56:11.000Z', + updatedAt: '2023-07-29T02:33:20.000Z', + createdById: 8, + updatedById: 4, + testdataid: 55, + orgcode: '310055', + orgname: '睿有限责任公司', + address: '75 Huaxia St, Jinghua Shangquan', + phone: '760-789-1994', + email: 'skudo73@outlook.com', + url: 'http://image.kenta1.com/ToolsHomeDecoration', + staffnum: 101, + regcapital: 168.9348, + paidcapital: 242.9576, + insuranceratio: 28.11, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3006', 'I3008', 'I3014', 'L2095', 'M2070'], + status_radio: '8', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3012', 'I3014', 'I3034', 'L2095', 'M2070', 'O3010'], + establishdate: '2021-12-29T21:52:28.000Z', + }, + { + createdAt: '2023-08-14T14:48:46.000Z', + updatedAt: '2023-08-08T22:23:16.000Z', + createdById: 5, + updatedById: 9, + testdataid: 84, + orgcode: '530084', + orgname: '璐有限责任公司', + address: '32 Jiangnan West Road, Haizhu District', + phone: '20-611-5899', + email: 'kffan@gmail.com', + url: 'http://auth.nakayama40.jp/Others', + staffnum: 27, + regcapital: 211.2634, + paidcapital: 251.2448, + insuranceratio: 8.95, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['F3134', 'I3006', 'I3008', 'I3027', 'I3032', 'P1029'], + status_radio: '5', + range_check: ['I3006', 'I3008', 'I3012', 'L2095', 'M2070'], + establishdate: '2022-12-20T04:42:54.000Z', + }, + { + createdAt: '2023-06-30T09:53:02.000Z', + updatedAt: '2023-07-18T22:24:59.000Z', + createdById: 3, + updatedById: 4, + testdataid: 56, + orgcode: '210056', + orgname: 'Lopez Brothers Technology LLC', + address: '延庆区028县道953号', + phone: '10-641-1187', + email: 'naks@hotmail.com', + url: 'https://image.guzh.info/CenturionGardenOutdoor', + staffnum: 185, + regcapital: 63.5178, + paidcapital: 153.6774, + insuranceratio: 91.75, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['I3008', 'I3010', 'I3012', 'I3014', 'I3034', 'M2070', 'P1029'], + status_radio: '4', + range_check: ['I3008', 'I3011', 'L2032', 'M2070', 'P1029'], + establishdate: '2021-05-21T11:02:59.000Z', + }, + { + createdAt: '2023-06-24T08:12:11.000Z', + updatedAt: '2023-07-10T02:25:11.743Z', + createdById: 8, + updatedById: null, + testdataid: 89, + orgcode: '640089', + orgname: 'Curtis Pharmaceutical Inc.', + address: null, + phone: '179-9231-4455', + email: 'aoi110@icloud.com', + url: 'https://www.yuziyi.co.jp/ToolsHomeDecoration', + staffnum: 119, + regcapital: 196.5298, + paidcapital: 195.5359, + insuranceratio: null, + isenable: null, + status_singleselect: '4', + range_multipleselect: ['F3134', 'I3012', 'I3014', 'I3027', 'L2095', 'O3010', 'P1029'], + status_radio: '4', + range_check: ['I3006', 'I3008', 'I3010', 'I3011', 'I3014', 'I3027', 'L2032', 'M2070'], + establishdate: '2023-10-08T21:08:37.000Z', + }, + { + createdAt: '2023-08-20T12:48:08.000Z', + updatedAt: '2023-06-13T11:27:58.000Z', + createdById: 7, + updatedById: 5, + testdataid: 44, + orgcode: '410044', + orgname: 'Lee Brothers Inc.', + address: '成华区双庆路300号', + phone: null, + email: 'tajiehong602@outlook.com', + url: 'http://image.tonkay9.info/Baby', + staffnum: 176, + regcapital: 297.447, + paidcapital: 16.0008, + insuranceratio: 69.5, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['F3134', 'I3007', 'I3011', 'I3014', 'I3034', 'M2070'], + status_radio: '3', + range_check: ['F3134', 'I3006', 'I3010', 'I3027', 'I3034', 'L2032', 'O3010'], + establishdate: '2023-01-04T12:48:43.000Z', + }, + { + createdAt: '2023-03-10T11:17:24.000Z', + updatedAt: '2023-07-25T07:52:00.000Z', + createdById: 3, + updatedById: 4, + testdataid: 35, + orgcode: '150035', + orgname: '邵記系统有限责任公司', + address: '209 1st Ave', + phone: '718-246-6197', + email: 'akat@hotmail.com', + url: 'http://video.fuchungyin1009.co.jp/ArtsHandicraftsSewing', + staffnum: 185, + regcapital: 262.7247, + paidcapital: 299.6833, + insuranceratio: 45.48, + isenable: null, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3007', 'I3014', 'I3027', 'I3032', 'I3034', 'L2032', 'M2070', 'O3010'], + status_radio: '2', + range_check: ['I3006', 'I3007', 'I3010', 'I3032', 'I3034', 'M2070', 'P1029'], + establishdate: '2022-06-04T09:44:46.000Z', + }, + { + createdAt: '2023-05-22T02:10:55.000Z', + updatedAt: '2023-07-19T22:44:41.000Z', + createdById: 9, + updatedById: 3, + testdataid: 4, + orgcode: '430004', + orgname: '宇宁制药有限责任公司', + address: '328 Jingtian East 1st St, Futian District', + phone: '755-3659-2323', + email: 'cmsi92@icloud.com', + url: 'http://www.czhennan4.biz/ToolsHomeDecoration', + staffnum: 132, + regcapital: 175.7828, + paidcapital: 290.7203, + insuranceratio: 45.74, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3010', 'I3011', 'I3012', 'I3027', 'M2070'], + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3007', 'I3012', 'I3014', 'I3032', 'L2095', 'P1029'], + establishdate: '2021-01-08T16:46:38.000Z', + }, + { + createdAt: '2023-03-08T16:04:01.000Z', + updatedAt: '2023-05-05T22:57:27.000Z', + createdById: 4, + updatedById: 3, + testdataid: 81, + orgcode: '110081', + orgname: '子异有限责任公司', + address: '229 Rush Street', + phone: '312-600-3390', + email: 'lokwy@icloud.com', + url: 'https://image.anakagawa.us/MusicalInstrument', + staffnum: 26, + regcapital: null, + paidcapital: 186.0802, + insuranceratio: 18.24, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3012', 'I3014', 'I3032', 'L2032', 'L2095', 'M2070', 'O3010'], + status_radio: '5', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3012', 'I3027', 'L2032', 'O3010'], + establishdate: '2021-04-08T05:24:21.000Z', + }, + { + createdAt: '2023-06-15T13:47:48.000Z', + updatedAt: '2023-05-20T12:37:39.000Z', + createdById: 0, + updatedById: 4, + testdataid: 34, + orgcode: '210034', + orgname: '董記电子有限责任公司', + address: '265 Huaxia St, Jinghua Shangquan', + phone: null, + email: 'rivashle1114@gmail.com', + url: 'http://auth.takeda1.cn/Handcrafts', + staffnum: 176, + regcapital: 281.1833, + paidcapital: 67.1949, + insuranceratio: 29.93, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3012', 'I3027', 'I3034', 'L2032', 'M2070', 'O3010', 'P1029'], + status_radio: '2', + range_check: ['F3134', 'I3006', 'I3007', 'I3011', 'I3012', 'I3014', 'L2095', 'O3010'], + establishdate: '2023-02-08T16:44:29.000Z', + }, + { + createdAt: '2023-08-04T08:58:50.000Z', + updatedAt: '2023-08-04T04:34:11.000Z', + createdById: 9, + updatedById: 8, + testdataid: 87, + orgcode: '420087', + orgname: '史記物业代理有限责任公司', + address: '549 Ridgewood Road', + phone: '330-982-5220', + email: 'maruyamashi@hotmail.com', + url: 'https://image.kondohik1.co.jp/ToysGames', + staffnum: 126, + regcapital: 227.7387, + paidcapital: 139.9423, + insuranceratio: 80.19, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3008', 'I3010', 'I3011', 'I3027', 'I3034', 'P1029'], + status_radio: '3', + range_check: ['I3006', 'I3010', 'I3014', 'I3032', 'I3034', 'M2070', 'O3010'], + establishdate: '2023-04-08T00:21:42.000Z', + }, + { + createdAt: '2023-06-17T22:39:01.000Z', + updatedAt: '2023-05-12T07:34:03.000Z', + createdById: 0, + updatedById: 7, + testdataid: 65, + orgcode: '250065', + orgname: '林有限责任公司', + address: '595 3rd Section Hongxing Road, Jinjiang District', + phone: '147-2716-7825', + email: 'kathy09@gmail.com', + url: 'http://auth.shenj.net/AppsGames', + staffnum: 146, + regcapital: 63.0539, + paidcapital: 18.8649, + insuranceratio: 0.22, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['I3008', 'I3011', 'I3012', 'I3027', 'L2032', 'L2095', 'M2070', 'P1029'], + status_radio: '1', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3011', 'I3027', 'I3032', 'L2032', 'L2095'], + establishdate: '2023-08-06T08:03:27.000Z', + }, + { + createdAt: '2023-04-26T07:55:22.000Z', + updatedAt: '2023-06-15T15:44:16.000Z', + createdById: 8, + updatedById: 4, + testdataid: 74, + orgcode: '240074', + orgname: "Schmidt's Food Inc.", + address: '罗湖区蔡屋围深南东路630号', + phone: '755-938-8862', + email: 'jialun9@outlook.com', + url: 'https://www.kwokwing614.org/ToysGames', + staffnum: 34, + regcapital: 291.2407, + paidcapital: 235.8852, + insuranceratio: 5.3, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3012', 'L2032', 'L2095', 'M2070'], + status_radio: '5', + range_check: ['F3134', 'I3006', 'I3010', 'I3011', 'I3014', 'I3032', 'M2070', 'O3010', 'P1029'], + establishdate: '2021-06-15T18:47:52.000Z', + }, + { + createdAt: '2023-06-13T04:08:45.000Z', + updatedAt: '2023-05-19T22:14:25.000Z', + createdById: 9, + updatedById: 4, + testdataid: 19, + orgcode: '650019', + orgname: 'Myers Brothers Telecommunication Inc.', + address: '294 Dong Zhi Men, Dongcheng District', + phone: '155-7317-7341', + email: 'daisukeotsu@mail.com', + url: 'https://drive.floresla8.org/BeautyPersonalCare', + staffnum: 128, + regcapital: 197.9632, + paidcapital: 90.5853, + insuranceratio: 64.9, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3007', 'I3008', 'I3014', 'L2095', 'P1029'], + status_radio: '2', + range_check: ['I3006', 'I3008', 'I3010', 'I3032', 'L2095', 'M2070'], + establishdate: '2023-04-30T14:56:20.000Z', + }, + { + createdAt: '2023-03-22T19:09:18.000Z', + updatedAt: '2023-08-25T18:37:39.000Z', + createdById: 7, + updatedById: 5, + testdataid: 21, + orgcode: '620021', + orgname: 'Williams Consultants Inc.', + address: '786 Tremont Road', + phone: '614-644-3993', + email: 'aota96@yahoo.com', + url: 'https://image.hilldo.biz/BeautyPersonalCare', + staffnum: 195, + regcapital: 94.6208, + paidcapital: 113.2067, + insuranceratio: 0.42, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3006', 'I3010', 'I3011', 'I3027', 'I3032', 'I3034'], + status_radio: '6', + range_check: ['F3134', 'I3007', 'I3008', 'I3012', 'I3014', 'I3027', 'I3034', 'L2032', 'M2070'], + establishdate: '2021-06-14T09:07:34.000Z', + }, + { + createdAt: '2023-08-28T21:13:03.000Z', + updatedAt: '2023-07-17T22:47:34.000Z', + createdById: 8, + updatedById: 6, + testdataid: 43, + orgcode: '220043', + orgname: 'Mendez Logistic LLC', + address: '浦东新区健祥路790号', + phone: '172-1090-3775', + email: 'suzukidaisuke@mail.com', + url: 'https://www.zhengr.info/Books', + staffnum: 173, + regcapital: 173.8671, + paidcapital: null, + insuranceratio: 72.41, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['I3011', 'I3032', 'I3034', 'L2032', 'L2095', 'P1029'], + status_radio: '8', + range_check: [ + 'F3134', + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3014', + 'I3032', + 'L2095', + 'M2070', + 'P1029', + ], + establishdate: '2021-12-27T04:23:26.000Z', + }, + { + createdAt: '2023-08-25T08:01:03.000Z', + updatedAt: '2023-07-10T02:25:11.739Z', + createdById: 4, + updatedById: null, + testdataid: 33, + orgcode: '230033', + orgname: '詩涵电脑有限责任公司', + address: '825 2nd Zhongshan Road, Yuexiu District', + phone: '166-7145-2872', + email: null, + url: 'http://video.scy.info/BeautyPersonalCare', + staffnum: 184, + regcapital: 237.116, + paidcapital: 7.2748, + insuranceratio: 66.74, + isenable: null, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3011', 'I3012', 'I3027', 'I3032', 'L2032', 'O3010'], + status_radio: '5', + range_check: null, + establishdate: '2021-10-10T20:57:37.000Z', + }, + { + createdAt: '2023-07-09T05:39:52.000Z', + updatedAt: '2023-06-26T04:30:41.000Z', + createdById: 6, + updatedById: 3, + testdataid: 10, + orgcode: '230010', + orgname: 'Medina Brothers Software LLC', + address: '472 West Market Street', + phone: '330-026-0212', + email: 'murphygrace@yahoo.com', + url: 'https://www.ngws.com/Books', + staffnum: 17, + regcapital: 74.6449, + paidcapital: 281.953, + insuranceratio: 26.67, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3008', 'I3011', 'I3014', 'I3034', 'L2032', 'M2070', 'P1029'], + status_radio: '8', + range_check: ['F3134', 'I3007', 'I3008', 'I3011', 'I3012', 'I3014', 'I3027', 'L2095', 'P1029'], + establishdate: '2023-01-25T11:54:37.000Z', + }, + { + createdAt: '2023-08-15T23:23:20.000Z', + updatedAt: '2023-05-09T00:00:10.000Z', + createdById: 1, + updatedById: 3, + testdataid: 36, + orgcode: '440036', + orgname: '李記电子有限责任公司', + address: '750 Lark Street', + phone: '838-441-5793', + email: 'ryonishimura530@gmail.com', + url: 'https://image.cwk.biz/Baby', + staffnum: 88, + regcapital: 292.6699, + paidcapital: 223.948, + insuranceratio: 36.35, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3006', 'I3007', 'I3010', 'I3011', 'I3012', 'I3032', 'I3034', 'L2032', 'L2095', 'P1029'], + status_radio: '4', + range_check: ['I3007', 'I3008', 'I3012', 'I3014', 'L2032', 'O3010'], + establishdate: null, + }, + { + createdAt: '2023-03-24T11:13:11.000Z', + updatedAt: '2023-06-02T14:17:58.000Z', + createdById: 10, + updatedById: 4, + testdataid: 51, + orgcode: '550051', + orgname: '胡有限责任公司', + address: '西城区复兴门内大街438号', + phone: '137-8041-7460', + email: 'saumankwo@mail.com', + url: 'https://auth.csoto.net/Others', + staffnum: null, + regcapital: 51.757, + paidcapital: 67.2645, + insuranceratio: 29.38, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'I3034'], + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3008', 'I3010', 'I3011', 'I3014', 'I3032', 'L2032', 'O3010', 'P1029'], + establishdate: '2021-01-02T11:44:41.000Z', + }, + { + createdAt: '2023-04-02T04:03:53.000Z', + updatedAt: '2023-06-14T12:56:19.000Z', + createdById: 6, + updatedById: 1, + testdataid: 59, + orgcode: '210059', + orgname: '陶記制药有限责任公司', + address: null, + phone: '179-0776-0684', + email: 'yuto426@gmail.com', + url: 'http://drive.pati.co.jp/ArtsHandicraftsSewing', + staffnum: 154, + regcapital: null, + paidcapital: 78.4742, + insuranceratio: 51.93, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3010', 'I3011', 'I3012', 'I3014', 'M2070', 'O3010', 'P1029'], + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3007', 'I3010', 'I3027', 'I3032', 'I3034', 'L2095', 'M2070'], + establishdate: '2023-01-31T01:55:31.000Z', + }, + { + createdAt: '2023-03-02T08:06:09.000Z', + updatedAt: '2023-07-10T02:25:11.741Z', + createdById: 7, + updatedById: null, + testdataid: 73, + orgcode: '410073', + orgname: '晓明有限责任公司', + address: '696 North Michigan Ave', + phone: '312-545-7935', + email: 'jocox1229@outlook.com', + url: null, + staffnum: 181, + regcapital: 208.5642, + paidcapital: 124.9526, + insuranceratio: 88.64, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['F3134', 'I3006', 'I3008', 'I3010', 'I3014', 'I3034', 'L2095', 'P1029'], + status_radio: '2', + range_check: ['F3134', 'I3006', 'I3008', 'I3010', 'I3011', 'I3014', 'I3027', 'I3032', 'L2095', 'M2070', 'O3010'], + establishdate: null, + }, + { + createdAt: '2023-03-25T22:18:26.000Z', + updatedAt: '2023-06-27T06:57:51.000Z', + createdById: 5, + updatedById: 3, + testdataid: 5, + orgcode: '630005', + orgname: "Grant's Network Systems LLC", + address: '成华区双庆路397号', + phone: '149-8059-2712', + email: 'turneal@gmail.com', + url: 'http://auth.fujr.jp/Food', + staffnum: 193, + regcapital: 116.4198, + paidcapital: 267.387, + insuranceratio: 62.02, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3008', 'I3010', 'I3012', 'I3014', 'I3032', 'I3034', 'L2032'], + status_radio: '6', + range_check: ['I3006', 'I3027', 'L2095', 'P1029'], + establishdate: '2023-09-09T15:50:04.000Z', + }, + { + createdAt: '2023-07-13T05:22:20.000Z', + updatedAt: '2023-08-11T12:32:14.000Z', + createdById: 3, + updatedById: 6, + testdataid: 29, + orgcode: '440029', + orgname: 'Thomas Brothers Pharmaceutical Inc.', + address: '472 West Market Street', + phone: '330-960-6930', + email: 'shirleypay@yahoo.com', + url: 'http://auth.jiang1103.jp/CollectiblesFineArt', + staffnum: 68, + regcapital: 4.9611, + paidcapital: 142.3588, + insuranceratio: 40.34, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3008', 'I3010', 'I3034', 'L2032', 'M2070', 'O3010'], + status_radio: '3', + range_check: ['F3134', 'I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'O3010', 'P1029'], + establishdate: '2021-09-05T07:53:50.000Z', + }, + { + createdAt: '2023-08-04T12:20:48.000Z', + updatedAt: '2023-05-02T21:09:34.000Z', + createdById: 3, + updatedById: 4, + testdataid: 95, + orgcode: '350095', + orgname: '陈記有限责任公司', + address: '268 Bergen St', + phone: '718-862-1720', + email: 'yara1953@gmail.com', + url: 'https://auth.riverae.jp/PetSupplies', + staffnum: 66, + regcapital: 282.8759, + paidcapital: 67.7269, + insuranceratio: 46.59, + isenable: null, + status_singleselect: '6', + range_multipleselect: ['I3006', 'I3007', 'I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'L2032', 'M2070'], + status_radio: '3', + range_check: ['I3006', 'I3010', 'I3011', 'I3014', 'I3027', 'I3034', 'P1029'], + establishdate: '2021-11-25T14:56:54.000Z', + }, + { + createdAt: '2023-05-01T17:43:16.000Z', + updatedAt: '2023-08-20T08:30:36.000Z', + createdById: 1, + updatedById: 4, + testdataid: 8, + orgcode: '540008', + orgname: 'Christine Communications Inc.', + address: '630 Canal Street', + phone: '212-162-6691', + email: 'hazelcraw@yahoo.com', + url: 'http://auth.jialunhu.xyz/Others', + staffnum: 47, + regcapital: 140.3844, + paidcapital: 236.435, + insuranceratio: 51.21, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3014', 'I3032', 'L2095', 'O3010'], + status_radio: '7', + range_check: ['I3008', 'I3010', 'I3032', 'L2032', 'M2070', 'O3010'], + establishdate: '2021-11-09T15:13:33.000Z', + }, + { + createdAt: '2023-06-20T03:38:25.000Z', + updatedAt: '2023-06-28T02:29:27.000Z', + createdById: 10, + updatedById: 5, + testdataid: 57, + orgcode: '130057', + orgname: 'Bobby Inc.', + address: '631 Huaxia St, Jinghua Shangquan', + phone: '167-3235-8699', + email: 'zhend@gmail.com', + url: 'http://drive.yunxi2015.biz/ClothingShoesandJewelry', + staffnum: 161, + regcapital: 20.9988, + paidcapital: 188.2148, + insuranceratio: 74.21, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['F3134', 'I3006', 'I3010', 'I3011', 'I3012', 'I3014', 'I3034', 'L2032', 'O3010', 'P1029'], + status_radio: '3', + range_check: null, + establishdate: '2023-04-27T11:30:53.000Z', + }, + { + createdAt: '2023-04-21T22:18:05.000Z', + updatedAt: '2023-05-02T02:52:18.000Z', + createdById: 4, + updatedById: 8, + testdataid: 60, + orgcode: '440060', + orgname: 'Wilson Engineering LLC', + address: '894 Jianxiang Rd, Pudong', + phone: '147-8272-9832', + email: 'hmwong@icloud.com', + url: 'https://drive.ayano303.jp/Beauty', + staffnum: null, + regcapital: 129.6063, + paidcapital: 211.5024, + insuranceratio: 50.02, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['I3010', 'I3012', 'I3027', 'I3034', 'L2032', 'M2070'], + status_radio: '2', + range_check: ['I3006', 'I3007', 'I3010', 'I3011', 'I3012', 'I3034', 'L2032', 'L2095', 'M2070', 'P1029'], + establishdate: '2023-02-02T04:12:05.000Z', + }, + { + createdAt: '2023-03-07T17:22:42.000Z', + updatedAt: '2023-05-17T00:30:59.000Z', + createdById: 9, + updatedById: 7, + testdataid: 28, + orgcode: '140028', + orgname: '嘉伦有限责任公司', + address: '189 West Market Street', + phone: '330-159-6349', + email: 'hotheod@gmail.com', + url: 'https://drive.warren98.biz/CellPhonesAccessories', + staffnum: 95, + regcapital: 35.1485, + paidcapital: 60.3831, + insuranceratio: 21.62, + isenable: null, + status_singleselect: '6', + range_multipleselect: [ + 'F3134', + 'I3006', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3032', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + 'P1029', + ], + status_radio: '5', + range_check: ['F3134', 'I3008', 'I3010', 'I3011', 'I3034', 'P1029'], + establishdate: '2022-01-13T23:52:05.000Z', + }, + { + createdAt: '2023-04-26T13:15:06.000Z', + updatedAt: '2023-07-13T07:01:14.000Z', + createdById: 1, + updatedById: 1, + testdataid: 49, + orgcode: '150049', + orgname: 'Owens Brothers Software LLC', + address: '422 W Ring Rd, Buji Town, Longgang', + phone: '755-057-2623', + email: 'zhanglan@gmail.com', + url: 'http://www.sotogregory502.org/ArtsHandicraftsSewing', + staffnum: 130, + regcapital: 46.1374, + paidcapital: 3.4208, + insuranceratio: 80.43, + isenable: null, + status_singleselect: '7', + range_multipleselect: ['I3007', 'I3012', 'I3027', 'I3032', 'L2095', 'O3010', 'P1029'], + status_radio: '4', + range_check: ['F3134', 'I3007', 'I3010', 'I3011', 'I3027', 'I3032', 'I3034', 'L2032'], + establishdate: '2022-11-07T06:43:42.000Z', + }, + { + createdAt: '2023-08-25T07:59:57.000Z', + updatedAt: '2023-08-30T00:27:44.000Z', + createdById: 0, + updatedById: 8, + testdataid: 12, + orgcode: '150012', + orgname: '张記有限责任公司', + address: '531 4th Section Renmin South Road, Jinjiang District', + phone: '28-987-2142', + email: 'tsubasayamaz5@outlook.com', + url: null, + staffnum: 188, + regcapital: 282.0915, + paidcapital: 230.6067, + insuranceratio: 24.06, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['I3011', 'I3012', 'L2032', 'L2095', 'M2070', 'O3010', 'P1029'], + status_radio: '7', + range_check: ['F3134', 'I3007', 'I3008', 'I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'I3034', 'P1029'], + establishdate: '2021-01-18T20:54:21.000Z', + }, + { + createdAt: '2023-08-06T11:00:11.000Z', + updatedAt: '2023-07-10T02:25:11.737Z', + createdById: null, + updatedById: null, + testdataid: 47, + orgcode: '620047', + orgname: 'Williams Brothers LLC', + address: '东泰五街621号', + phone: null, + email: 'martin2002@icloud.com', + url: 'https://auth.kwokwaiman.us/HouseholdKitchenAppliances', + staffnum: 78, + regcapital: 279.4844, + paidcapital: 266.3442, + insuranceratio: 32.12, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3010', 'I3011', 'I3027', 'L2032'], + status_radio: '8', + range_check: ['F3134', 'I3006', 'I3007', 'I3010', 'I3011', 'I3012', 'I3027', 'I3034', 'L2032', 'L2095', 'O3010'], + establishdate: '2022-08-14T17:55:17.000Z', + }, + { + createdAt: '2023-08-03T18:16:07.000Z', + updatedAt: '2023-05-11T02:43:43.000Z', + createdById: 0, + updatedById: 9, + testdataid: 69, + orgcode: '610069', + orgname: '致远有限责任公司', + address: '龙岗区学园一巷968号', + phone: '755-7571-7551', + email: 'ynakagawa925@icloud.com', + url: 'http://video.tzhiyuan.net/SportsOutdoor', + staffnum: 90, + regcapital: 2.7332, + paidcapital: 108.5354, + insuranceratio: 64.76, + isenable: null, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3011', 'I3012', 'I3034', 'O3010'], + status_radio: '2', + range_check: ['I3007', 'I3008', 'I3010', 'I3012', 'I3014', 'I3032'], + establishdate: '2021-06-18T12:50:21.000Z', + }, + { + createdAt: '2023-05-16T16:27:22.000Z', + updatedAt: '2023-05-13T11:17:34.000Z', + createdById: 5, + updatedById: 6, + testdataid: 80, + orgcode: '140080', + orgname: '睿贸易有限责任公司', + address: '白云区小坪东路90号', + phone: '197-2431-7197', + email: 'wuwm@icloud.com', + url: 'http://drive.hz4.com/HealthBabyCare', + staffnum: 36, + regcapital: 132.0943, + paidcapital: 278.8981, + insuranceratio: 40.03, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3011', 'I3032', 'L2032', 'L2095', 'M2070'], + status_radio: '8', + range_check: ['I3006', 'I3007', 'I3011', 'I3014', 'I3034', 'L2095'], + establishdate: '2021-07-12T22:04:31.000Z', + }, + { + createdAt: '2023-03-16T02:59:56.000Z', + updatedAt: '2023-07-03T00:12:24.000Z', + createdById: 4, + updatedById: 6, + testdataid: 45, + orgcode: '120045', + orgname: 'Adams Brothers Inc.', + address: '847 West Houston Street', + phone: '212-675-9018', + email: null, + url: 'https://image.ks4.info/Handcrafts', + staffnum: null, + regcapital: 179.2233, + paidcapital: 203.447, + insuranceratio: 70.8, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3008', 'I3010', 'I3012', 'I3014', 'I3027', 'L2095', 'M2070', 'O3010'], + status_radio: '8', + range_check: ['F3134', 'I3008', 'I3012', 'I3014', 'I3027', 'I3032', 'L2095', 'M2070', 'P1029'], + establishdate: '2021-03-27T22:05:05.000Z', + }, + { + createdAt: '2023-07-17T15:00:25.000Z', + updatedAt: '2023-05-22T19:30:52.000Z', + createdById: 8, + updatedById: 9, + testdataid: 85, + orgcode: '310085', + orgname: '秀英系统有限责任公司', + address: '492 Pedway', + phone: null, + email: 'huyu1@outlook.com', + url: 'http://image.shilan.net/ArtsHandicraftsSewing', + staffnum: null, + regcapital: 224.7069, + paidcapital: null, + insuranceratio: 27.21, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3008', 'I3012', 'I3014', 'I3034', 'L2095', 'M2070', 'P1029'], + status_radio: '1', + range_check: [ + 'F3134', + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3032', + 'I3034', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + ], + establishdate: '2021-03-30T21:18:22.000Z', + }, + { + createdAt: '2023-04-02T16:10:12.000Z', + updatedAt: '2023-07-10T02:25:11.740Z', + createdById: 7, + updatedById: null, + testdataid: 72, + orgcode: '350072', + orgname: '邱記电讯有限责任公司', + address: '白云区机场路棠苑街五巷951号', + phone: '175-9040-0888', + email: 'chiuwaiwong@gmail.com', + url: null, + staffnum: 162, + regcapital: 59.8279, + paidcapital: 141.9588, + insuranceratio: 86.98, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3010', 'I3011', 'I3027', 'I3034', 'O3010'], + status_radio: '6', + range_check: null, + establishdate: '2022-01-31T21:23:17.000Z', + }, + { + createdAt: '2023-05-07T01:59:43.000Z', + updatedAt: '2023-08-13T07:41:11.000Z', + createdById: 4, + updatedById: 10, + testdataid: 100, + orgcode: '310100', + orgname: '陈記发展贸易有限责任公司', + address: '环区南街二巷453号', + phone: '769-5777-9743', + email: 'siu817@icloud.com', + url: 'https://auth.donfreem506.org/SportsOutdoor', + staffnum: 193, + regcapital: 264.7365, + paidcapital: 32.0125, + insuranceratio: 46.66, + isenable: null, + status_singleselect: '7', + range_multipleselect: null, + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3011', 'I3027', 'I3034', 'O3010', 'P1029'], + establishdate: '2023-05-23T04:27:32.000Z', + }, + { + createdAt: '2023-08-01T08:19:39.000Z', + updatedAt: '2023-07-17T02:48:36.000Z', + createdById: 3, + updatedById: 5, + testdataid: 30, + orgcode: '550030', + orgname: '子异技术有限责任公司', + address: '成华区二仙桥东三路698号', + phone: null, + email: null, + url: 'https://drive.patterson44.net/ArtsHandicraftsSewing', + staffnum: null, + regcapital: 88.4317, + paidcapital: 232.3953, + insuranceratio: 88.86, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['I3007', 'L2032', 'L2095', 'M2070', 'P1029'], + status_radio: '7', + range_check: ['F3134', 'I3011', 'I3032'], + establishdate: '2022-01-26T08:14:41.000Z', + }, + { + createdAt: '2023-07-20T08:54:02.000Z', + updatedAt: '2023-06-15T20:12:54.000Z', + createdById: 6, + updatedById: 8, + testdataid: 75, + orgcode: '310075', + orgname: '武記有限责任公司', + address: '545 Rush Street', + phone: '312-022-1358', + email: 'xiuying1125@hotmail.com', + url: 'https://video.zhezi.jp/Books', + staffnum: 197, + regcapital: 136.3425, + paidcapital: 96.297, + insuranceratio: 28.79, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['I3008', 'I3012', 'I3034', 'O3010', 'P1029'], + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3007', 'I3014', 'I3027', 'I3032', 'L2095', 'M2070'], + establishdate: '2021-01-26T06:55:36.000Z', + }, + { + createdAt: '2023-07-12T18:10:51.000Z', + updatedAt: '2023-08-19T13:15:44.000Z', + createdById: 3, + updatedById: 1, + testdataid: 23, + orgcode: '420023', + orgname: '秀英有限责任公司', + address: null, + phone: '330-890-8118', + email: 'grahadiana1127@icloud.com', + url: 'https://auth.kkin.org/CellPhonesAccessories', + staffnum: 152, + regcapital: 190.0139, + paidcapital: 274.6092, + insuranceratio: 6.39, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3008', 'I3010', 'I3011', 'I3012', 'I3027', 'L2032', 'O3010', 'P1029'], + status_radio: '2', + range_check: ['I3006', 'I3007', 'I3010', 'I3012', 'I3014', 'I3032', 'I3034', 'L2095', 'M2070'], + establishdate: '2023-06-21T21:47:05.000Z', + }, + { + createdAt: '2023-06-30T09:34:24.000Z', + updatedAt: '2023-07-10T02:25:11.728Z', + createdById: null, + updatedById: 3, + testdataid: 26, + orgcode: '520026', + orgname: '钱記有限责任公司', + address: '106 S Broadway', + phone: null, + email: 'hla53@gmail.com', + url: 'http://drive.songzhiyuan.jp/SportsOutdoor', + staffnum: 128, + regcapital: 45.8576, + paidcapital: 49.0852, + insuranceratio: 69.17, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['F3134', 'I3010', 'L2095', 'P1029'], + status_radio: '5', + range_check: ['F3134', 'I3006', 'I3008', 'I3010', 'I3027', 'I3032', 'L2095', 'O3010'], + establishdate: '2022-03-22T20:41:25.000Z', + }, + { + createdAt: '2023-05-24T19:59:54.000Z', + updatedAt: '2023-08-20T10:07:16.000Z', + createdById: 8, + updatedById: 3, + testdataid: 37, + orgcode: '620037', + orgname: 'Cindy Logistic Inc.', + address: '791 Columbia St', + phone: '718-173-2935', + email: 'satoyuna2@yahoo.com', + url: 'https://www.yunxi8.cn/MusicalInstrument', + staffnum: 6, + regcapital: 39.6587, + paidcapital: 118.6882, + insuranceratio: 25.94, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3007', 'I3008', 'I3012', 'L2032', 'L2095', 'O3010'], + status_radio: '5', + range_check: null, + establishdate: null, + }, + { + createdAt: '2023-07-10T12:04:12.000Z', + updatedAt: '2023-07-10T02:25:11.742Z', + createdById: 6, + updatedById: null, + testdataid: 78, + orgcode: '140078', + orgname: '陆記有限责任公司', + address: '578 Xiaoping E Rd, Baiyun ', + phone: '155-0484-9694', + email: 'fukudahikari@gmail.com', + url: 'https://auth.gibsondon.us/CollectiblesFineArt', + staffnum: 79, + regcapital: 21.868, + paidcapital: 252.1746, + insuranceratio: 58.72, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3010', 'I3032', 'L2032', 'L2095', 'O3010', 'P1029'], + status_radio: '5', + range_check: ['F3134', 'I3008', 'I3010', 'I3012', 'I3027', 'I3034', 'L2032', 'M2070', 'P1029'], + establishdate: '2021-04-16T16:14:38.000Z', + }, + { + createdAt: '2023-07-05T20:02:07.000Z', + updatedAt: '2023-05-27T21:16:46.000Z', + createdById: 10, + updatedById: 5, + testdataid: 71, + orgcode: '630071', + orgname: '晓明有限责任公司', + address: "19 West Chang'an Avenue, Xicheng District", + phone: '156-1137-8442', + email: 'sakurai46@icloud.com', + url: 'https://auth.xiaoming1223.cn/Beauty', + staffnum: 82, + regcapital: 250.4023, + paidcapital: 186.6815, + insuranceratio: 95.41, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['F3134', 'I3008', 'I3010', 'I3011', 'I3012', 'I3027', 'I3034', 'L2032', 'P1029'], + status_radio: '7', + range_check: ['I3006', 'I3007', 'I3010', 'I3011', 'I3014', 'I3027', 'I3032', 'I3034', 'L2032', 'O3010'], + establishdate: '2022-11-27T14:02:00.000Z', + }, + { + createdAt: '2023-04-03T11:41:58.000Z', + updatedAt: '2023-07-25T07:08:25.000Z', + createdById: 4, + updatedById: 5, + testdataid: 53, + orgcode: '420053', + orgname: '睿工业有限责任公司', + address: '914 Xiaoping E Rd, Baiyun ', + phone: '20-439-3781', + email: 'lucimoreno@outlook.com', + url: 'https://www.lanwang1968.biz/BaggageTravelEquipment', + staffnum: 138, + regcapital: 176.73, + paidcapital: 238.5599, + insuranceratio: 0.32, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3008', 'I3014', 'I3027', 'I3032', 'L2032', 'O3010'], + status_radio: '1', + range_check: ['I3007', 'I3008', 'I3010', 'I3011', 'I3027', 'M2070', 'O3010', 'P1029'], + establishdate: '2021-04-22T09:11:54.000Z', + }, + { + createdAt: '2023-04-12T12:17:12.000Z', + updatedAt: '2023-08-28T05:02:11.000Z', + createdById: 3, + updatedById: 5, + testdataid: 70, + orgcode: '620070', + orgname: '唐有限责任公司', + address: '449 East Alley', + phone: '614-258-2477', + email: 'swtong17@gmail.com', + url: 'https://www.colesherr5.us/FilmSupplies', + staffnum: 42, + regcapital: 232.1782, + paidcapital: 280.8157, + insuranceratio: 20.07, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3007', 'I3008', 'I3012', 'I3034', 'L2095', 'P1029'], + status_radio: '6', + range_check: ['I3010', 'I3027', 'I3032', 'O3010'], + establishdate: '2023-10-29T00:43:12.000Z', + }, + { + createdAt: '2023-04-13T23:19:56.000Z', + updatedAt: '2023-08-08T03:08:04.000Z', + createdById: 3, + updatedById: 6, + testdataid: 9, + orgcode: '630009', + orgname: '刘有限责任公司', + address: '917 Alameda Street', + phone: '213-743-6878', + email: 'huimeiwong1127@icloud.com', + url: 'https://image.rinkond1.co.jp/IndustrialScientificSupplies', + staffnum: 190, + regcapital: 264.891, + paidcapital: 249.9184, + insuranceratio: 54.71, + isenable: true, + status_singleselect: '8', + range_multipleselect: null, + status_radio: '3', + range_check: ['F3134', 'I3007', 'I3010', 'I3012', 'I3014', 'I3032', 'I3034', 'L2095', 'P1029'], + establishdate: '2023-08-22T00:30:45.000Z', + }, + { + createdAt: '2023-06-21T00:53:37.000Z', + updatedAt: '2023-05-24T10:30:53.000Z', + createdById: 1, + updatedById: 10, + testdataid: 24, + orgcode: '140024', + orgname: '秀英有限责任公司', + address: '208 North Michigan Ave', + phone: '312-222-4942', + email: 'sitls@outlook.com', + url: 'https://auth.ededward.co.jp/ClothingShoesandJewelry', + staffnum: null, + regcapital: 11.602, + paidcapital: 289.2036, + insuranceratio: 19.3, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['I3007', 'I3010', 'I3011', 'I3012', 'I3032', 'I3034', 'L2095', 'O3010'], + status_radio: '2', + range_check: ['F3134', 'I3007', 'I3012', 'I3014', 'I3027', 'L2032', 'O3010', 'P1029'], + establishdate: '2023-04-14T10:31:47.000Z', + }, + { + createdAt: '2023-08-20T18:44:46.000Z', + updatedAt: '2023-07-09T19:37:01.000Z', + createdById: 7, + updatedById: 4, + testdataid: 15, + orgcode: '150015', + orgname: 'Doris LLC', + address: '海珠区江南西路205号', + phone: '152-0553-7772', + email: 'onkay5@icloud.com', + url: 'http://auth.yuningshi.cn/Baby', + staffnum: 51, + regcapital: 62.3067, + paidcapital: 243.9179, + insuranceratio: 65.83, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['I3007', 'I3014', 'I3032', 'L2095', 'M2070', 'P1029'], + status_radio: '8', + range_check: ['I3007', 'I3010', 'I3011', 'I3014', 'I3032', 'M2070', 'O3010'], + establishdate: '2021-12-17T06:08:17.000Z', + }, + { + createdAt: '2023-07-08T09:41:18.000Z', + updatedAt: '2023-08-04T03:25:41.000Z', + createdById: 9, + updatedById: 8, + testdataid: 11, + orgcode: '230011', + orgname: 'Alexander Trading Inc.', + address: '21 4th Section Renmin South Road, Jinjiang District', + phone: '28-964-2526', + email: 'stewart324@gmail.com', + url: null, + staffnum: 74, + regcapital: 205.9118, + paidcapital: 252.7905, + insuranceratio: 32.57, + isenable: true, + status_singleselect: '4', + range_multipleselect: ['F3134', 'I3006', 'I3010', 'I3011', 'I3014', 'I3027', 'I3032', 'L2095', 'M2070'], + status_radio: '4', + range_check: ['F3134', 'I3006', 'I3010', 'I3011', 'I3034', 'L2032', 'L2095', 'M2070', 'O3010', 'P1029'], + establishdate: '2022-10-25T03:57:53.000Z', + }, + { + createdAt: '2023-06-10T01:08:46.000Z', + updatedAt: '2023-08-18T23:07:28.000Z', + createdById: 6, + updatedById: 8, + testdataid: 58, + orgcode: '340058', + orgname: 'Frank Software LLC', + address: '天河区天河路330号', + phone: '165-2214-3345', + email: 'crystortiz8@icloud.com', + url: 'https://auth.richardlee7.co.jp/ClothingShoesandJewelry', + staffnum: 180, + regcapital: 17.056, + paidcapital: 156.8234, + insuranceratio: 67.28, + isenable: null, + status_singleselect: '1', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3011', 'I3032', 'L2032', 'L2095', 'O3010'], + status_radio: '6', + range_check: ['F3134', 'I3006', 'I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'I3034', 'L2032', 'L2095'], + establishdate: '2021-02-13T02:03:49.000Z', + }, + { + createdAt: '2023-07-01T15:28:34.000Z', + updatedAt: '2023-07-10T02:25:11.726Z', + createdById: null, + updatedById: 5, + testdataid: 39, + orgcode: '510039', + orgname: '龚电讯有限责任公司', + address: '365 Central Avenue', + phone: '838-434-4757', + email: 'barnicholas@gmail.com', + url: 'http://www.lokht1970.info/CDsVinyl', + staffnum: 83, + regcapital: 21.7374, + paidcapital: 120.7973, + insuranceratio: 47.33, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3007', 'I3011', 'I3034', 'L2095'], + status_radio: '7', + range_check: ['I3006', 'I3032', 'L2095', 'P1029'], + establishdate: '2022-01-02T12:32:43.000Z', + }, + { + createdAt: '2023-04-03T15:27:23.000Z', + updatedAt: '2023-05-29T18:51:28.000Z', + createdById: 1, + updatedById: 10, + testdataid: 18, + orgcode: '120018', + orgname: '胡工程有限责任公司', + address: '279 Zhongshan 5th Rd, Zimaling Shangquan', + phone: '190-4080-9140', + email: 'guanqi@icloud.com', + url: 'http://www.nancyha5.us/CDsVinyl', + staffnum: 112, + regcapital: 144.4962, + paidcapital: 145.2596, + insuranceratio: 14.18, + isenable: null, + status_singleselect: '1', + range_multipleselect: null, + status_radio: '5', + range_check: ['F3134', 'I3008', 'I3014', 'M2070', 'O3010'], + establishdate: '2023-04-21T15:43:03.000Z', + }, + { + createdAt: '2023-06-18T11:53:30.000Z', + updatedAt: '2023-08-03T11:12:45.000Z', + createdById: 0, + updatedById: 7, + testdataid: 79, + orgcode: '130079', + orgname: '卢記工业有限责任公司', + address: '坑美十五巷202号', + phone: '769-6447-1441', + email: null, + url: 'https://www.simmons96.org/Appliances', + staffnum: 144, + regcapital: 207.3241, + paidcapital: 128.2934, + insuranceratio: 13.18, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['I3007', 'I3008', 'I3011', 'I3014', 'I3027', 'I3034', 'L2032', 'L2095'], + status_radio: '7', + range_check: ['I3006', 'I3008', 'I3010', 'I3014', 'M2070', 'O3010'], + establishdate: '2021-05-10T10:57:24.000Z', + }, + { + createdAt: '2023-08-16T10:27:52.000Z', + updatedAt: '2023-07-10T02:25:11.725Z', + createdById: null, + updatedById: 4, + testdataid: 63, + orgcode: '320063', + orgname: 'Perry Pharmaceutical LLC', + address: '房山区岳琉路956号', + phone: null, + email: 'kwyin302@hotmail.com', + url: 'http://video.wingfatcha7.us/ComputersElectronics', + staffnum: 169, + regcapital: 143.6456, + paidcapital: 17.5641, + insuranceratio: 49.78, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3007', 'I3008', 'I3027', 'I3034', 'L2095', 'M2070'], + status_radio: '2', + range_check: ['F3134', 'I3006', 'I3014', 'I3027', 'I3032', 'I3034', 'L2032', 'L2095'], + establishdate: '2021-04-22T09:53:07.000Z', + }, + { + createdAt: '2023-07-10T19:32:09.000Z', + updatedAt: '2023-07-10T02:25:11.732Z', + createdById: null, + updatedById: 7, + testdataid: 94, + orgcode: '430094', + orgname: 'Aguilar LLC', + address: '710 1st Ave', + phone: '718-677-4225', + email: 'mchic@outlook.com', + url: 'http://www.wtimothy.info/AppsGames', + staffnum: 158, + regcapital: 229.4348, + paidcapital: 248.8399, + insuranceratio: 82.42, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['I3006', 'I3008', 'I3010', 'I3011', 'I3012', 'I3014', 'L2032', 'P1029'], + status_radio: '1', + range_check: [ + 'I3006', + 'I3007', + 'I3008', + 'I3012', + 'I3014', + 'I3027', + 'I3032', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + 'P1029', + ], + establishdate: '2021-04-20T23:37:47.000Z', + }, + { + createdAt: '2023-07-01T04:31:41.000Z', + updatedAt: '2023-05-06T19:48:18.000Z', + createdById: 6, + updatedById: 4, + testdataid: 90, + orgcode: '640090', + orgname: 'Maria Communications LLC', + address: '東城区東直門內大街160号', + phone: '10-4155-4174', + email: 'keithug@gmail.com', + url: 'https://auth.ayahasegawa9.net/Others', + staffnum: 195, + regcapital: 24.8107, + paidcapital: 92.6292, + insuranceratio: 67.85, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3007', 'I3008', 'I3014', 'I3027', 'I3032', 'I3034', 'L2095', 'M2070', 'P1029'], + status_radio: '2', + range_check: ['I3006', 'I3007', 'I3011', 'I3012', 'I3014', 'I3027', 'L2032', 'M2070', 'P1029'], + establishdate: '2023-06-19T18:33:58.000Z', + }, + { + createdAt: '2023-04-30T13:55:12.000Z', + updatedAt: '2023-07-10T02:25:11.739Z', + createdById: 10, + updatedById: null, + testdataid: 61, + orgcode: '640061', + orgname: '秀英工程有限责任公司', + address: '锦江区人民南路四段972号', + phone: '28-8229-5494', + email: 'kkakeung@hotmail.com', + url: 'https://image.daisauman.co.jp/BaggageTravelEquipment', + staffnum: 169, + regcapital: 8.3627, + paidcapital: 267.237, + insuranceratio: 41.35, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3007', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'L2032', 'L2095', 'P1029'], + status_radio: '1', + range_check: ['F3134', 'I3006', 'I3008', 'I3010', 'I3014', 'I3027', 'I3034', 'L2032', 'P1029'], + establishdate: '2021-09-20T16:20:50.000Z', + }, + { + createdAt: '2023-04-04T06:33:16.000Z', + updatedAt: '2023-06-11T22:08:56.000Z', + createdById: 3, + updatedById: 4, + testdataid: 1, + orgcode: '320001', + orgname: '子异有限责任公司', + address: '锦江区人民南路四段831号', + phone: '188-8510-5193', + email: 'yamadaayato907@yahoo.com', + url: 'http://video.merichardson5.biz/SportsOutdoor', + staffnum: 87, + regcapital: 224.0227, + paidcapital: 288.8069, + insuranceratio: 0.75, + isenable: true, + status_singleselect: '1', + range_multipleselect: [ + 'I3006', + 'I3007', + 'I3010', + 'I3011', + 'I3012', + 'I3027', + 'I3034', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + ], + status_radio: '1', + range_check: ['F3134', 'I3007', 'I3010', 'I3012', 'I3032', 'L2032', 'M2070'], + establishdate: '2022-12-03T17:27:35.000Z', + }, + { + createdAt: '2023-04-02T06:15:56.000Z', + updatedAt: '2023-06-20T05:21:38.000Z', + createdById: 3, + updatedById: 4, + testdataid: 38, + orgcode: '450038', + orgname: "Mills's Toy Inc.", + address: '成华区二仙桥东三路904号', + phone: '28-3185-9051', + email: 'moria@icloud.com', + url: 'https://video.sekat.net/ComputersElectronics', + staffnum: 39, + regcapital: 173.4007, + paidcapital: 38.577, + insuranceratio: 84.44, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3011', 'I3032', 'L2095', 'M2070', 'P1029'], + status_radio: '7', + range_check: null, + establishdate: '2022-05-21T01:29:54.000Z', + }, + { + createdAt: '2023-03-13T10:50:26.000Z', + updatedAt: '2023-06-01T14:40:47.000Z', + createdById: 6, + updatedById: 6, + testdataid: 14, + orgcode: '420014', + orgname: '安琪制药有限责任公司', + address: '986 1st Ave', + phone: '718-947-3048', + email: 'powm@yahoo.com', + url: 'http://drive.chengsw.co.jp/AutomotivePartsAccessories', + staffnum: 174, + regcapital: 142.9716, + paidcapital: 80.9565, + insuranceratio: 23.87, + isenable: null, + status_singleselect: '4', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3010', 'I3011', 'I3012', 'I3027', 'I3032', 'L2032', 'L2095'], + status_radio: '1', + range_check: ['F3134', 'I3006', 'I3010', 'I3014', 'I3027', 'I3032', 'L2032'], + establishdate: '2022-04-02T23:44:50.000Z', + }, + { + createdAt: '2023-05-01T17:47:37.000Z', + updatedAt: '2023-07-11T11:57:11.000Z', + createdById: 1, + updatedById: 6, + testdataid: 7, + orgcode: '120007', + orgname: 'Dawn Consultants Inc.', + address: '西城区西長安街924号', + phone: '10-368-3528', + email: 'onkayya@yahoo.com', + url: 'http://auth.che7.us/PetSupplies', + staffnum: 18, + regcapital: 118.5422, + paidcapital: 26.4247, + insuranceratio: 3.54, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['I3006', 'I3007', 'I3014', 'I3027', 'I3032', 'L2095', 'O3010', 'P1029'], + status_radio: '2', + range_check: ['F3134', 'I3010', 'I3012', 'I3014', 'I3027', 'I3034', 'L2032', 'P1029'], + establishdate: '2023-08-06T11:29:00.000Z', + }, + { + createdAt: '2023-06-27T04:27:55.000Z', + updatedAt: '2023-07-22T04:49:39.000Z', + createdById: 5, + updatedById: 3, + testdataid: 96, + orgcode: '210096', + orgname: 'Samuel Electronic Inc.', + address: '53 Dong Zhi Men, Dongcheng District', + phone: '169-1441-5592', + email: 'xuyuning@mail.com', + url: 'https://auth.wu76.cn/Baby', + staffnum: 195, + regcapital: 39.701, + paidcapital: 207.4876, + insuranceratio: 94.68, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3027', 'I3032', 'L2095', 'M2070', 'P1029'], + status_radio: '8', + range_check: ['I3006', 'I3034', 'L2032', 'L2095'], + establishdate: '2021-05-01T13:11:18.000Z', + }, + { + createdAt: '2023-06-12T05:43:49.000Z', + updatedAt: '2023-07-17T08:15:07.000Z', + createdById: 6, + updatedById: 5, + testdataid: 40, + orgcode: '230040', + orgname: 'Johnny LLC', + address: '788 Shennan Ave, Futian District', + phone: '755-593-2881', + email: 'hjo@mail.com', + url: null, + staffnum: 137, + regcapital: 24.5385, + paidcapital: 250.5823, + insuranceratio: 52.25, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3011', 'I3014', 'I3032', 'L2032', 'L2095', 'M2070'], + status_radio: '4', + range_check: ['I3007', 'I3010', 'I3014', 'I3027', 'I3032', 'I3034', 'L2095', 'M2070', 'O3010', 'P1029'], + establishdate: '2023-11-17T21:46:14.000Z', + }, + { + createdAt: '2023-06-15T06:41:01.000Z', + updatedAt: '2023-06-17T00:51:49.000Z', + createdById: 8, + updatedById: 5, + testdataid: 42, + orgcode: '630042', + orgname: 'Griffin Brothers Logistic Inc.', + address: '197 Grape Street', + phone: '213-126-7992', + email: 'zitaowan1971@outlook.com', + url: 'https://image.parkerdiane.net/Others', + staffnum: 26, + regcapital: 251.177, + paidcapital: 292.6008, + insuranceratio: 79.99, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3011', 'I3034', 'L2032', 'L2095', 'O3010', 'P1029'], + status_radio: '1', + range_check: ['F3134', 'I3011', 'I3012', 'I3027', 'I3032', 'P1029'], + establishdate: '2022-10-06T20:13:52.000Z', + }, + { + createdAt: '2023-03-04T18:03:54.000Z', + updatedAt: '2023-08-07T00:59:00.000Z', + createdById: 5, + updatedById: 1, + testdataid: 52, + orgcode: '650052', + orgname: '杜工程有限责任公司', + address: '924 Wooster Street', + phone: '212-656-1851', + email: 'sakamoto303@icloud.com', + url: 'http://drive.waiman1946.biz/ClothingShoesandJewelry', + staffnum: 149, + regcapital: 85.2739, + paidcapital: 47.86, + insuranceratio: 5.24, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['I3007', 'I3008', 'I3012', 'I3014', 'I3027', 'I3032', 'I3034', 'L2095', 'M2070', 'P1029'], + status_radio: '8', + range_check: ['I3007', 'I3010', 'I3012', 'I3027', 'I3032', 'I3034', 'L2032', 'L2095', 'M2070'], + establishdate: '2021-04-22T06:26:45.000Z', + }, + { + createdAt: '2023-04-01T16:13:17.000Z', + updatedAt: '2023-05-22T09:24:17.000Z', + createdById: 8, + updatedById: 10, + testdataid: 6, + orgcode: '630006', + orgname: 'Jacob Telecommunication LLC', + address: '东泰五街968号', + phone: '769-163-0302', + email: 'ikiw@gmail.com', + url: 'http://www.wmsiu.cn/ClothingShoesandJewelry', + staffnum: 129, + regcapital: 28.8318, + paidcapital: 17.9883, + insuranceratio: 87.39, + isenable: true, + status_singleselect: '1', + range_multipleselect: ['I3006', 'I3007', 'I3008', 'I3012', 'I3014', 'I3032', 'L2032', 'O3010', 'P1029'], + status_radio: '3', + range_check: ['F3134', 'I3006', 'I3010', 'I3011', 'I3027', 'I3032', 'L2095', 'M2070'], + establishdate: '2023-04-11T23:07:14.000Z', + }, + { + createdAt: '2023-03-15T15:19:15.000Z', + updatedAt: '2023-07-10T02:25:11.742Z', + createdById: 8, + updatedById: null, + testdataid: 83, + orgcode: '440083', + orgname: 'Linda Software LLC', + address: '福田区景田东一街36号', + phone: '193-8669-1290', + email: 'mio10@outlook.com', + url: 'https://video.joe306.org/Handcrafts', + staffnum: 123, + regcapital: 127.33, + paidcapital: 209.9907, + insuranceratio: 96.56, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['I3006', 'I3012', 'I3027', 'L2095', 'M2070', 'O3010', 'P1029'], + status_radio: '3', + range_check: [ + 'F3134', + 'I3006', + 'I3007', + 'I3008', + 'I3011', + 'I3012', + 'I3027', + 'I3032', + 'I3034', + 'L2095', + 'M2070', + 'O3010', + ], + establishdate: '2021-10-24T08:00:26.000Z', + }, + { + createdAt: '2023-08-08T08:50:58.000Z', + updatedAt: '2023-07-30T08:37:12.000Z', + createdById: 1, + updatedById: 4, + testdataid: 64, + orgcode: '650064', + orgname: '范物业代理有限责任公司', + address: '459 Xue Yuan Yi Xiang, Longgang', + phone: '755-2232-1934', + email: 'leswhite229@outlook.com', + url: 'http://auth.nakanaoshi1954.biz/BeautyPersonalCare', + staffnum: 167, + regcapital: 50.6322, + paidcapital: 253.2695, + insuranceratio: 74.53, + isenable: null, + status_singleselect: '4', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3008', 'I3010', 'I3012', 'I3027', 'I3034', 'O3010'], + status_radio: '5', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3032', 'I3034', 'L2032', 'L2095', 'M2070', 'P1029'], + establishdate: '2021-09-28T05:02:42.000Z', + }, + { + createdAt: '2023-03-13T20:21:27.000Z', + updatedAt: '2023-07-10T02:25:11.738Z', + createdById: 7, + updatedById: null, + testdataid: 50, + orgcode: '420050', + orgname: 'Helen Electronic Inc.', + address: '891 028 County Rd, Yanqing District', + phone: '10-8030-8564', + email: 'linr1955@icloud.com', + url: 'http://www.rossanna908.xyz/SportsOutdoor', + staffnum: 149, + regcapital: 143.496, + paidcapital: null, + insuranceratio: 40.72, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3011', 'I3027', 'I3032', 'I3034', 'L2032', 'O3010'], + status_radio: '5', + range_check: ['I3006', 'I3008', 'I3010', 'I3011', 'I3012', 'I3032', 'M2070', 'O3010'], + establishdate: '2023-11-27T06:02:42.000Z', + }, + { + createdAt: '2023-08-22T10:45:00.000Z', + updatedAt: '2023-07-06T22:27:32.000Z', + createdById: 5, + updatedById: 9, + testdataid: 2, + orgcode: '120002', + orgname: 'Black Technology LLC', + address: '71 Pedway', + phone: '312-418-7933', + email: 'aarimura9@outlook.com', + url: 'http://auth.patel03.biz/HealthBabyCare', + staffnum: 95, + regcapital: 70.7385, + paidcapital: 96.6588, + insuranceratio: 74.47, + isenable: true, + status_singleselect: '7', + range_multipleselect: [ + 'F3134', + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3014', + 'I3027', + 'I3032', + 'M2070', + 'O3010', + 'P1029', + ], + status_radio: '7', + range_check: ['F3134', 'I3007', 'I3011', 'I3012', 'I3014', 'I3032', 'I3034', 'L2095', 'P1029'], + establishdate: '2021-02-18T13:44:21.000Z', + }, + { + createdAt: '2023-07-24T13:03:58.000Z', + updatedAt: '2023-08-15T23:59:06.000Z', + createdById: 0, + updatedById: 10, + testdataid: 76, + orgcode: '340076', + orgname: 'Jeff Inc.', + address: '珊瑚路357号', + phone: '769-5438-3615', + email: 'ikk53@gmail.com', + url: 'http://www.chimingyeung.info/CellPhonesAccessories', + staffnum: 127, + regcapital: 12.9611, + paidcapital: 177.9844, + insuranceratio: 98.28, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['F3134', 'I3007', 'I3010', 'I3027', 'L2032', 'L2095', 'O3010', 'P1029'], + status_radio: '4', + range_check: [ + 'I3006', + 'I3007', + 'I3008', + 'I3011', + 'I3012', + 'I3027', + 'I3032', + 'I3034', + 'L2032', + 'L2095', + 'O3010', + 'P1029', + ], + establishdate: '2021-02-20T10:36:54.000Z', + }, + { + createdAt: '2023-06-01T02:52:18.000Z', + updatedAt: '2023-07-05T07:00:07.000Z', + createdById: 0, + updatedById: 10, + testdataid: 16, + orgcode: '410016', + orgname: '卢系统有限责任公司', + address: '670 Figueroa Street', + phone: '213-571-5321', + email: 'shiry@gmail.com', + url: 'https://video.yishihan64.xyz/Food', + staffnum: 63, + regcapital: 127.0708, + paidcapital: 25.9392, + insuranceratio: 89.87, + isenable: null, + status_singleselect: '5', + range_multipleselect: ['I3006', 'I3008', 'I3010', 'I3011', 'I3027', 'I3032', 'L2095'], + status_radio: '3', + range_check: ['I3008', 'I3014', 'I3032', 'L2095', 'P1029'], + establishdate: '2022-03-30T15:28:56.000Z', + }, + { + createdAt: '2023-08-09T02:19:55.000Z', + updatedAt: '2023-07-10T02:25:11.730Z', + createdById: null, + updatedById: 3, + testdataid: 98, + orgcode: '350098', + orgname: 'King Brothers Telecommunication Inc.', + address: '582 Diplomacy Drive', + phone: '614-785-5349', + email: 'lindadams922@gmail.com', + url: 'https://www.luwu.biz/ToolsHomeDecoration', + staffnum: 146, + regcapital: 237.6197, + paidcapital: 295.3728, + insuranceratio: 92, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['I3007', 'I3014', 'I3027', 'L2095', 'O3010'], + status_radio: '7', + range_check: ['I3006', 'I3008', 'I3010', 'I3012', 'I3027', 'L2095', 'O3010', 'P1029'], + establishdate: '2022-09-04T04:56:52.000Z', + }, + { + createdAt: '2023-03-03T14:11:21.000Z', + updatedAt: '2023-06-10T07:54:44.000Z', + createdById: 10, + updatedById: 8, + testdataid: 82, + orgcode: '650082', + orgname: '子韬食品有限责任公司', + address: '889 Alameda Street', + phone: '213-000-0993', + email: 'goto1004@icloud.com', + url: 'https://drive.kucy84.org/FilmSupplies', + staffnum: 164, + regcapital: null, + paidcapital: 75.458, + insuranceratio: 56.84, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3006', 'I3007', 'I3012', 'I3014', 'I3027', 'I3034', 'L2095', 'M2070', 'P1029'], + status_radio: '1', + range_check: ['F3134', 'I3010', 'I3012', 'I3014', 'I3027', 'I3032', 'L2032', 'L2095'], + establishdate: null, + }, + { + createdAt: '2023-05-04T12:45:29.000Z', + updatedAt: '2023-05-29T15:14:39.000Z', + createdById: 4, + updatedById: 4, + testdataid: 32, + orgcode: '210032', + orgname: '杰宏贸易有限责任公司', + address: '695 Fifth Avenue', + phone: '212-301-3048', + email: 'tianrui6@mail.com', + url: 'https://video.fat12.us/AppsGames', + staffnum: 196, + regcapital: 253.0279, + paidcapital: 201.5416, + insuranceratio: 26.71, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['F3134', 'I3006', 'I3007', 'I3014', 'I3032', 'I3034', 'M2070'], + status_radio: '7', + range_check: [ + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3032', + 'I3034', + 'L2032', + 'L2095', + 'O3010', + 'P1029', + ], + establishdate: '2022-11-10T12:49:21.000Z', + }, + { + createdAt: '2023-06-20T22:11:44.000Z', + updatedAt: '2023-07-01T22:29:47.000Z', + createdById: 0, + updatedById: 6, + testdataid: 88, + orgcode: '550088', + orgname: 'Thomas Engineering LLC', + address: 'No.24, Dongsan Road, Erxianqiao, Chenghua District', + phone: '178-8435-2008', + email: 'rachelj5@icloud.com', + url: 'http://www.dulan.us/VideoGames', + staffnum: 49, + regcapital: 57.0454, + paidcapital: 79.007, + insuranceratio: 84.03, + isenable: null, + status_singleselect: '7', + range_multipleselect: ['I3007', 'I3010', 'I3011', 'I3032', 'I3034', 'L2095', 'M2070', 'O3010'], + status_radio: '1', + range_check: ['I3007', 'I3012', 'I3027', 'I3034', 'L2095'], + establishdate: '2022-02-23T22:58:42.000Z', + }, + { + createdAt: '2023-03-28T22:03:18.000Z', + updatedAt: '2023-06-03T12:48:50.000Z', + createdById: 4, + updatedById: 8, + testdataid: 3, + orgcode: '430003', + orgname: '彭記电子有限责任公司', + address: '白云区小坪东路412号', + phone: '155-6842-3057', + email: 'ctingfung@mail.com', + url: 'http://image.yan2007.org/FilmSupplies', + staffnum: 158, + regcapital: 15.9472, + paidcapital: 73.3624, + insuranceratio: 67.76, + isenable: true, + status_singleselect: '6', + range_multipleselect: ['F3134', 'I3010', 'I3011', 'I3012', 'I3027'], + status_radio: '5', + range_check: ['I3006', 'I3007', 'I3008', 'I3012', 'I3027', 'M2070', 'O3010', 'P1029'], + establishdate: '2023-02-18T04:02:21.000Z', + }, + { + createdAt: '2023-06-15T07:46:48.000Z', + updatedAt: '2023-05-01T19:53:39.000Z', + createdById: 6, + updatedById: 3, + testdataid: 92, + orgcode: '510092', + orgname: '邱記贸易有限责任公司', + address: '930 Rush Street', + phone: '312-781-8750', + email: 'xiujia1961@mail.com', + url: 'http://image.lamwingfat.us/ComputersElectronics', + staffnum: 76, + regcapital: null, + paidcapital: 282.6866, + insuranceratio: 76.86, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['I3006', 'I3007', 'I3011', 'I3027', 'L2032', 'P1029'], + status_radio: '7', + range_check: ['F3134', 'I3010', 'I3014', 'I3032', 'L2095', 'P1029'], + establishdate: '2023-11-18T06:44:55.000Z', + }, + { + createdAt: '2023-06-09T17:49:18.000Z', + updatedAt: '2023-07-10T02:25:11.735Z', + createdById: null, + updatedById: null, + testdataid: 68, + orgcode: '450068', + orgname: '汪記有限责任公司', + address: '坑美十五巷585号', + phone: '769-680-4019', + email: null, + url: 'https://video.lok2000.org/IndustrialScientificSupplies', + staffnum: 139, + regcapital: 90.9114, + paidcapital: 284.0137, + insuranceratio: 21.71, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['I3006', 'I3010', 'I3012', 'I3027', 'I3032', 'L2095', 'O3010'], + status_radio: '7', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3012', 'I3014', 'I3027', 'I3034', 'L2032', 'L2095'], + establishdate: '2021-11-03T10:14:09.000Z', + }, + { + createdAt: '2023-06-16T05:04:59.000Z', + updatedAt: '2023-06-16T07:08:56.000Z', + createdById: 6, + updatedById: 9, + testdataid: 54, + orgcode: '310054', + orgname: "Martinez's Logistic LLC", + address: '476 Yueliu Rd, Fangshan District', + phone: '10-971-0570', + email: 'yogoto@hotmail.com', + url: 'https://www.kimuram2.co.jp/CDsVinyl', + staffnum: 132, + regcapital: 80.6528, + paidcapital: 87.4258, + insuranceratio: 87.58, + isenable: true, + status_singleselect: '7', + range_multipleselect: ['I3006', 'I3010', 'I3011', 'I3032', 'I3034', 'L2032', 'L2095', 'M2070', 'O3010', 'P1029'], + status_radio: '3', + range_check: ['I3006', 'I3007', 'I3008', 'I3012', 'I3032', 'L2095', 'M2070', 'O3010', 'P1029'], + establishdate: '2021-08-11T08:08:33.000Z', + }, + { + createdAt: '2023-07-17T20:41:03.000Z', + updatedAt: '2023-06-24T05:16:44.000Z', + createdById: 5, + updatedById: 9, + testdataid: 93, + orgcode: '350093', + orgname: '安琪有限责任公司', + address: '712 Huaxia St, Jinghua Shangquan', + phone: '138-0406-7670', + email: 'zhennanm@yahoo.com', + url: 'http://drive.akinamori803.cn/Food', + staffnum: 186, + regcapital: 37.836, + paidcapital: 53.0391, + insuranceratio: 74.62, + isenable: true, + status_singleselect: '2', + range_multipleselect: ['I3006', 'I3012', 'I3014', 'I3027', 'O3010'], + status_radio: '2', + range_check: ['I3007', 'I3010', 'I3011', 'I3012', 'I3034', 'M2070', 'O3010'], + establishdate: '2023-06-28T09:10:55.000Z', + }, + { + createdAt: '2023-05-11T11:07:26.000Z', + updatedAt: '2023-06-20T19:16:26.000Z', + createdById: 9, + updatedById: 9, + testdataid: 13, + orgcode: '310013', + orgname: 'Evans Brothers LLC', + address: '黄浦区淮海中路683号', + phone: '164-7632-7777', + email: 'aokon@gmail.com', + url: 'http://image.shawm9.us/CellPhonesAccessories', + staffnum: 97, + regcapital: 182.859, + paidcapital: 105.8903, + insuranceratio: 63.85, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['F3134', 'I3006', 'I3008', 'I3012', 'I3014', 'L2032', 'O3010'], + status_radio: '5', + range_check: ['I3008', 'I3010', 'I3012', 'L2095'], + establishdate: '2021-09-05T13:35:37.000Z', + }, + { + createdAt: '2023-03-10T11:27:35.000Z', + updatedAt: '2023-06-28T11:16:47.000Z', + createdById: 6, + updatedById: 4, + testdataid: 91, + orgcode: '240091', + orgname: '郑顾问有限责任公司', + address: '872 Canal Street', + phone: '212-005-7205', + email: 'rink4@mail.com', + url: 'https://image.jimenezt7.jp/Others', + staffnum: 167, + regcapital: 288.5176, + paidcapital: 33.3404, + insuranceratio: 55.87, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['I3011', 'I3027', 'I3034', 'L2032'], + status_radio: '8', + range_check: ['I3008', 'I3011', 'I3034', 'L2032', 'L2095', 'M2070', 'O3010'], + establishdate: null, + }, + { + createdAt: '2023-08-11T17:06:24.000Z', + updatedAt: '2023-06-24T22:14:32.000Z', + createdById: 3, + updatedById: 7, + testdataid: 17, + orgcode: '620017', + orgname: 'Patrick Technology LLC', + address: '505 Ridgewood Road', + phone: '330-035-6552', + email: 'nommis58@icloud.com', + url: 'http://auth.suitsuki304.net/Handcrafts', + staffnum: 196, + regcapital: 163.7404, + paidcapital: 14.0622, + insuranceratio: 25.51, + isenable: null, + status_singleselect: '3', + range_multipleselect: null, + status_radio: '2', + range_check: ['F3134', 'I3006', 'I3011', 'I3012', 'I3014', 'I3027', 'I3034', 'L2032', 'M2070', 'O3010', 'P1029'], + establishdate: '2022-08-06T12:16:45.000Z', + }, + { + createdAt: '2023-03-09T02:32:46.000Z', + updatedAt: '2023-07-02T05:29:43.000Z', + createdById: 3, + updatedById: 6, + testdataid: 46, + orgcode: '220046', + orgname: "Schmidt's Inc.", + address: '503 2nd Zhongshan Road, Yuexiu District', + phone: '20-272-7867', + email: 'ylla@outlook.com', + url: 'https://www.seikokudo.cn/Books', + staffnum: 131, + regcapital: 259.642, + paidcapital: 172.6541, + insuranceratio: 38.83, + isenable: true, + status_singleselect: '3', + range_multipleselect: ['I3006', 'I3007', 'I3010', 'I3012', 'I3027', 'I3034', 'L2095', 'M2070', 'P1029'], + status_radio: '7', + range_check: ['I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'I3034', 'M2070'], + establishdate: '2023-06-16T08:57:56.000Z', + }, + { + createdAt: '2023-04-02T02:35:24.000Z', + updatedAt: '2023-07-22T21:27:03.000Z', + createdById: 7, + updatedById: 5, + testdataid: 48, + orgcode: '530048', + orgname: "Long's LLC", + address: '成华区双庆路872号', + phone: '28-2897-5944', + email: 'koonwy@outlook.com', + url: 'https://auth.zj2017.org/Beauty', + staffnum: 22, + regcapital: 81.2412, + paidcapital: 89.2826, + insuranceratio: 22.58, + isenable: true, + status_singleselect: '8', + range_multipleselect: ['I3008', 'I3011', 'I3012', 'I3034', 'L2032'], + status_radio: '1', + range_check: ['F3134', 'I3006', 'I3008', 'I3011', 'I3012', 'I3034', 'L2032', 'M2070', 'O3010'], + establishdate: '2023-08-06T15:31:45.000Z', + }, + { + createdAt: '2023-08-06T02:38:26.000Z', + updatedAt: '2023-07-01T01:03:02.000Z', + createdById: 4, + updatedById: 6, + testdataid: 99, + orgcode: '310099', + orgname: 'Jamie Consultants LLC', + address: '罗湖区清水河一路156号', + phone: '755-7525-5040', + email: 'ikki922@icloud.com', + url: 'http://video.haki.info/ArtsHandicraftsSewing', + staffnum: 140, + regcapital: null, + paidcapital: 234.484, + insuranceratio: 55.76, + isenable: null, + status_singleselect: '1', + range_multipleselect: ['F3134', 'I3010', 'I3011', 'I3012', 'I3014', 'I3027', 'I3032', 'M2070', 'O3010'], + status_radio: '1', + range_check: [ + 'I3007', + 'I3010', + 'I3011', + 'I3014', + 'I3027', + 'I3032', + 'I3034', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + 'P1029', + ], + establishdate: '2022-10-17T10:43:37.000Z', + }, + { + createdAt: '2023-06-12T16:34:38.000Z', + updatedAt: '2023-08-03T11:31:10.000Z', + createdById: 8, + updatedById: 4, + testdataid: 22, + orgcode: '120022', + orgname: '子韬有限责任公司', + address: '442 Tangyuan Street 5th Alley, Airport Road, Baiyun', + phone: '168-8185-4258', + email: 'anqihu5@gmail.com', + url: 'http://auth.ontang10.xyz/CDsVinyl', + staffnum: 150, + regcapital: 191.8842, + paidcapital: 282.3322, + insuranceratio: 37.26, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['F3134', 'I3007', 'I3014', 'I3034', 'M2070', 'P1029'], + status_radio: '8', + range_check: [ + 'I3006', + 'I3007', + 'I3008', + 'I3010', + 'I3011', + 'I3012', + 'I3014', + 'I3027', + 'I3032', + 'L2032', + 'L2095', + 'M2070', + 'O3010', + 'P1029', + ], + establishdate: '2022-05-31T12:34:11.000Z', + }, + { + createdAt: '2023-04-26T08:00:47.000Z', + updatedAt: '2023-06-26T22:25:14.000Z', + createdById: 7, + updatedById: 7, + testdataid: 97, + orgcode: '120097', + orgname: '嘉伦有限责任公司', + address: '天河区大信商圈大信南路342号', + phone: '165-5165-2865', + email: 'gonzalez1221@gmail.com', + url: 'https://auth.kwansw901.biz/HouseholdKitchenAppliances', + staffnum: 34, + regcapital: 38.0234, + paidcapital: 180.0297, + insuranceratio: 10.93, + isenable: true, + status_singleselect: '5', + range_multipleselect: ['I3008', 'I3010', 'I3011', 'I3014', 'I3034', 'L2095', 'M2070', 'O3010', 'P1029'], + status_radio: '6', + range_check: ['F3134', 'I3007', 'I3011', 'I3012', 'I3014', 'I3027', 'L2032', 'M2070', 'O3010', 'P1029'], + establishdate: null, + }, + { + createdAt: '2023-04-20T07:20:03.000Z', + updatedAt: '2023-05-22T03:28:57.000Z', + createdById: 7, + updatedById: 3, + testdataid: 67, + orgcode: '240067', + orgname: 'Betty Telecommunication Inc.', + address: '351 Figueroa Street', + phone: '213-187-2091', + email: 'marthaferguson6@gmail.com', + url: 'https://www.taniguchihina1110.us/SportsOutdoor', + staffnum: 58, + regcapital: 188.0791, + paidcapital: 127.5241, + insuranceratio: 36.21, + isenable: null, + status_singleselect: '5', + range_multipleselect: ['I3006', 'I3010', 'I3014', 'I3027', 'L2032', 'O3010'], + status_radio: '1', + range_check: ['F3134', 'I3006', 'I3007', 'I3008', 'I3011', 'L2032', 'L2095', 'O3010'], + establishdate: '2023-01-11T04:04:18.000Z', + }, +]; +export default module.exports = { + appendJsonCollectionName, + builtinGeneral, + builtinFile, + builtinExpression, + generalWithNoRelationalFields, + generalWithNoRelationalFieldsData, +}; diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts new file mode 100644 index 000000000..6e9a16d36 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts @@ -0,0 +1,528 @@ +import { Locator, Page } from '@nocobase/test/e2e'; +export class CreateWorkFlow { + readonly page: Page; + name: Locator; + triggerType: Locator; + description: Locator; + autoDeleteHistory: Locator; + submitButton: Locator; + cancelButton: Locator; + constructor(page: Page) { + this.page = page; + this.name = page.getByLabel('block-item-CollectionField-workflows-Name').getByRole('textbox'); + this.triggerType = page.getByTestId('select-single'); + this.description = page.getByTestId('description-item').getByRole('textbox'); + this.autoDeleteHistory = page.getByTestId('select-multiple'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + } +} + +export class EditWorkFlow { + readonly page: Page; + name: Locator; + statusIsOn: Locator; + statusIisOff: Locator; + description: Locator; + autoDeleteHistory: Locator; + submitButton: Locator; + cancelButton: Locator; + constructor(page: Page, workFlowName: string) { + this.page = page; + this.name = page.getByLabel('block-item-CollectionField-workflows-Name').getByRole('textbox'); + this.statusIsOn = page.getByLabel('On', { exact: true }); + this.statusIisOff = page.getByLabel('Off'); + this.description = page.getByTestId('description-item').getByRole('textbox'); + this.autoDeleteHistory = page + .getByTestId('deleteExecutionOnStatus-item') + .getByTestId('antd-select') + .locator('div') + .nth(1); + this.submitButton = page.getByLabel(`action-Action-Submit-workflows-${workFlowName}`); + this.cancelButton = page.getByLabel(`action-Action-Cancel-workflows-${workFlowName}`); + } +} + +export class WorkflowManagement { + readonly page: Page; + addNewButton: Locator; + deleteButton: Locator; + filterButton: Locator; + constructor(page: Page) { + this.page = page; + this.addNewButton = page.getByLabel('action-Action-Add new-workflows'); + this.deleteButton = page.getByLabel('action-Action-Delete-workflows'); + this.filterButton = page.getByLabel('action-Filter.Action-Filter-filter-workflows'); + } +} + +export class WorkflowListRecords { + readonly page: Page; + executionCountPopup: Locator; + configureAction: Locator; + editAction: Locator; + duplicateAction: Locator; + deleteAction: Locator; + constructor(page: Page, workFlowName: string) { + this.page = page; + this.executionCountPopup = page.getByLabel(`executed-${workFlowName}`); + this.configureAction = page.getByLabel(`action-WorkflowLink-Configure-workflows-${workFlowName}`); + this.editAction = page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`); + this.duplicateAction = page.getByLabel(`action-Action.Link-Duplicate-workflows-${workFlowName}`); + this.deleteAction = page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`); + } +} + +export class ApprovalTriggerNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + checkWthdrawable: Locator; + configureUserInterfaceButton: Locator; + addBlockButton: Locator; + addApplyFormMenu: Locator; + configureFieldsButton: Locator; + configureActionsButton: Locator; + saveDraftSwitch: Locator; + addNodeButton: Locator; + constructor(page: Page, triggerName: string, collectionName: string) { + this.page = page; + this.node = page.getByText('TriggeraConfigure'); + this.nodeTitle = page.locator('textarea').filter({ hasText: triggerName }); + this.nodeConfigure = page.getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.checkWthdrawable = page.getByLabel('Withdrawable'); + this.configureUserInterfaceButton = page.getByRole('button', { name: 'Configure user interface' }); + this.addBlockButton = page.getByRole('button', { name: 'Add block' }); + this.addApplyFormMenu = page.getByRole('menuitem', { name: 'Apply form' }); + this.configureFieldsButton = page.getByTestId('configure-fields-button-of-form-item-' + collectionName); + this.configureActionsButton = page.getByTestId( + 'approval-trigger-configure-form-actions-add-action-button-' + collectionName, + ); + this.saveDraftSwitch = page.getByRole('menuitem', { name: 'Save draft' }).getByRole('switch'); + this.addNodeButton = page.getByLabel('add-button', { exact: true }); + } +} + +export class ApprovalNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + assigneesDropDown: Locator; + checkReturnable: Locator; + configureUserInterfaceButton: Locator; + addBlockButton: Locator; + addApplyFormMenu: Locator; + configureFieldsButton: Locator; + configureActionsButton: Locator; + saveDraftSwitch: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string, collectionName: string) { + this.page = page; + this.node = page.getByText('TriggeraConfigure'); + this.nodeTitle = page.locator('textarea').filter({ hasText: nodeName }); + this.nodeConfigure = page.getByRole('button', { name: 'Configure' }); + this.assigneesDropDown = page.getByLabel('Search'); + this.checkReturnable = page.getByLabel('Returnable'); + this.configureUserInterfaceButton = page.getByRole('button', { name: 'Configure user interface' }); + this.addBlockButton = page.getByTestId('add-block-button-in-workflow-workflows'); + this.addApplyFormMenu = page.getByRole('menuitem', { name: 'Apply form' }); + this.configureFieldsButton = page.getByTestId('configure-fields-button-of-form-item-' + collectionName); + this.configureActionsButton = page.getByTestId( + 'approval-trigger-configure-form-actions-add-action-button-' + collectionName, + ); + this.saveDraftSwitch = page.getByRole('menuitem', { name: 'Save draft' }).getByRole('switch'); + this.addNodeButton = page.getByLabel(`add-button-calculation-${nodeName}`, { exact: true }); + } +} + +export class ScheduleTriggerNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + customTimeTriggerOptions: Locator; + startTimeEntryBox: Locator; + RrpeatModeDropdown: Locator; + + dataTableTimeFieldOptions: Locator; + collectionDropDown: Locator; + startTimeDropdown: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, triggerName: string, collectionName: string) { + this.page = page; + this.node = page.getByText('TriggeraConfigure'); + this.nodeTitle = page.locator('textarea').filter({ hasText: triggerName }); + this.nodeConfigure = page.getByRole('button', { name: 'Configure' }); + this.customTimeTriggerOptions = page.getByLabel('Based on certain date'); + this.startTimeEntryBox = page.getByPlaceholder('Select date'); + this.RrpeatModeDropdown = page.getByLabel('block-item-RepeatField-workflows-Repeat mode'); + + this.dataTableTimeFieldOptions = page.getByLabel('Based on date field of collection'); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.startTimeDropdown = page.getByLabel('block-item-OnField-workflows-Starts on'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel('add-button', { exact: true }); + } +} + +export class CollectionTriggerNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + triggerOnDropdown: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, triggerName: string, collectionName: string) { + this.page = page; + this.node = page.getByLabel(`Trigger-${triggerName}`); + this.nodeTitle = page.getByLabel(`Trigger-${triggerName}`).getByRole('textbox'); + this.nodeConfigure = page.getByLabel(`Trigger-${triggerName}`).getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.triggerOnDropdown = page + .getByLabel('block-item-Select-workflows-Trigger on') + .getByRole('button', { name: 'Trigger on' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel('add-button', { exact: true }); + } +} + +export class FormEventTriggerNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + relationalDataDropdown: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, triggerName: string, collectionName: string) { + this.page = page; + this.node = page.getByLabel(`Trigger-${triggerName}`); + this.nodeTitle = page.getByLabel(`Trigger-${triggerName}`).getByRole('textbox'); + this.nodeConfigure = page.getByLabel(`Trigger-${triggerName}`).getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.relationalDataDropdown = page.getByTestId('select-field-Preload associations'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel('add-button', { exact: true }); + } +} + +export class ClculationNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + mathCalculationEngine: Locator; + formulaCalculationEngine: Locator; + calculationExpression: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Calculation-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Calculation-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Calculation-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.mathCalculationEngine = page.getByLabel('Math.js'); + this.formulaCalculationEngine = page.getByLabel('Formula.js'); + this.calculationExpression = page.getByLabel('textbox'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-calculation-${nodeName}`, { exact: true }); + } +} + +export class QueryRecordNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + allowMultipleDataBoxesForResults: Locator; + addSortFieldsButton: Locator; + pageNumberEditBox: Locator; + pageNumberVariableButton: Locator; + pageSizeEditBox: Locator; + exitProcessOptionsBoxWithEmptyResult: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Query record-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Query record-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Query record-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.allowMultipleDataBoxesForResults = page.getByLabel('Allow multiple records as'); + this.addSortFieldsButton = page.getByRole('button', { name: 'plus Add sort field' }); + this.pageNumberEditBox = page.getByLabel('variable-constant'); + this.pageNumberVariableButton = page.getByLabel('variable-button'); + this.pageSizeEditBox = page.getByLabel('block-item-InputNumber-workflows-Page size').getByRole('spinbutton'); + this.exitProcessOptionsBoxWithEmptyResult = page.getByLabel('Exit when query result is null'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-query-${nodeName}`, { exact: true }); + } +} + +export class CreateRecordNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + addFieldsButton: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Create record-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Create record-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Create record-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.addFieldsButton = page.getByRole('button', { name: 'plus Add field' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-create-${nodeName}`, { exact: true }); + } +} + +export class UpdateRecordNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + batchUpdateModeRadio: Locator; + articleByArticleUpdateModeRadio: Locator; + addFieldsButton: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Update record-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Update record-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Update record-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.batchUpdateModeRadio = page + .getByLabel('block-item-IndividualHooksRadioWithTooltip-workflows-Update mode') + .getByLabel('Update in a batch'); + this.articleByArticleUpdateModeRadio = page + .getByLabel('block-item-IndividualHooksRadioWithTooltip-workflows-Update mode') + .getByLabel('Update one by one'); + this.addFieldsButton = page.getByRole('button', { name: 'plus Add field' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-update-${nodeName}`, { exact: true }); + } +} + +export class DeleteRecordNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + collectionDropDown: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Delete record-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Delete record-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Delete record-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-delete-${nodeName}`, { exact: true }); + } +} + +export class AggregateNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + countRadio: Locator; + sumRadio: Locator; + avgRadio: Locator; + maxRadio: Locator; + minRadio: Locator; + dataTableDataRadio: Locator; + linkedDataTableDataRadio: Locator; + collectionDropDown: Locator; + aggregatedFieldDropDown: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Aggregate-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Aggregate-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Aggregate-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.countRadio = page.getByLabel('COUNT'); + this.sumRadio = page.getByLabel('SUM', { exact: true }); + this.avgRadio = page.getByLabel('AVG', { exact: true }); + this.maxRadio = page.getByLabel('MAX', { exact: true }); + this.minRadio = page.getByLabel('MIN', { exact: true }); + this.dataTableDataRadio = page.getByLabel('Data of collection'); + this.linkedDataTableDataRadio = page.getByLabel('Data of associated collection'); + this.collectionDropDown = page.getByRole('button', { name: 'Select collection' }); + // this.aggregatedFieldDropDown = page.getByLabel('block-item-FieldsSelect-workflows-Field to aggregate').getByRole('textbox').getByRole('combobox'); + this.aggregatedFieldDropDown = page.locator( + 'input.ant-select-selection-search-input[role="combobox"][aria-haspopup="listbox"]', + ); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-aggregate-${nodeName}`, { exact: true }); + } +} + +export class ManualNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + assigneesDropDown: Locator; + configureUserInterfaceButton: Locator; + addBlockButton: Locator; + triggerDataMenu: Locator; + nodeDataMenu: Locator; + customFormMenu: Locator; + createRecordFormMenu: Locator; + updateRecordFormMenu: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Manual-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Manual-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Manual-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.assigneesDropDown = page.getByTestId('select-single'); + this.configureUserInterfaceButton = page.getByRole('button', { name: 'Configure user interface' }); + this.addBlockButton = page.getByLabel('schema-initializer-Grid-AddBlockButton-workflows'); + this.triggerDataMenu = page.getByLabel('Data blocks-triggerData'); + this.nodeDataMenu = page.getByLabel('nodes', { exact: true }); + this.customFormMenu = page.getByRole('menuitem', { name: 'Custom form' }); + this.createRecordFormMenu = page.getByRole('menuitem', { name: 'Create record form' }); + this.updateRecordFormMenu = page.getByRole('menuitem', { name: 'Update record form' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-manual-${nodeName}`, { exact: true }); + } +} + +export class ConditionYesNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + basicRadio: Locator; + mathRadio: Locator; + formulaRadio: Locator; + submitButton: Locator; + cancelButton: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Condition-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Condition-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Condition-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + // await page.getByLabel('variable-constant').first().click(); + // await page.getByLabel('variable-button').first().click(); + // await page.getByLabel('select-operator-calc').first().click(); + // await page.getByRole('option', { name: '=' }).click(); + // await page.getByLabel('variable-constant').nth(1).click(); + // await page.getByLabel('variable-button').nth(1).click(); + this.basicRadio = page.getByLabel('Basic'); + this.mathRadio = page.getByLabel('Math.js'); + this.formulaRadio = page.getByLabel('Formula.js'); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-condition-${nodeName}`, { exact: true }); + } +} + +export class ConditionBranchNode { + readonly page: Page; + node: Locator; + nodeTitle: Locator; + nodeConfigure: Locator; + basicRadio: Locator; + mathRadio: Locator; + formulaRadio: Locator; + submitButton: Locator; + cancelButton: Locator; + addNoBranchNode: Locator; + addYesBranchNode: Locator; + addNodeButton: Locator; + constructor(page: Page, nodeName: string) { + this.page = page; + this.node = page.getByLabel(`Condition-${nodeName}`, { exact: true }); + this.nodeTitle = page.getByLabel(`Condition-${nodeName}`, { exact: true }).getByRole('textbox'); + this.nodeConfigure = page + .getByLabel(`Condition-${nodeName}`, { exact: true }) + .getByRole('button', { name: 'Configure' }); + this.submitButton = page.getByLabel('action-Action-Submit-workflows'); + this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); + this.addNodeButton = page.getByLabel(`add-button-condition-${nodeName}`, { exact: true }); + this.basicRadio = page.getByLabel('Basic'); + this.mathRadio = page.getByLabel('Math.js'); + this.formulaRadio = page.getByLabel('Formula.js'); + this.addNoBranchNode = page.getByLabel(`add-button-condition-${nodeName}-0`); + this.addYesBranchNode = page.getByLabel(`add-button-condition-${nodeName}-1`); + } +} + +export default module.exports = { + CreateWorkFlow, + EditWorkFlow, + WorkflowManagement, + WorkflowListRecords, + ApprovalTriggerNode, + ApprovalNode, + ScheduleTriggerNode, + CollectionTriggerNode, + FormEventTriggerNode, + ClculationNode, + QueryRecordNode, + CreateRecordNode, + UpdateRecordNode, + DeleteRecordNode, + AggregateNode, + ManualNode, + ConditionYesNode, + ConditionBranchNode, +}; diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eUtils.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eUtils.ts new file mode 100644 index 000000000..61159ae68 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2eUtils.ts @@ -0,0 +1,747 @@ +import { request } from '@nocobase/test/e2e'; + +const PORT = process.env.APP_PORT || 20000; +const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`; + +// 创建工作流 +export const apiCreateWorkflow = async (data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "current": true, + "options": { + "deleteExecutionOnStatus": [] + }, + "title": "t3", + "type": "collection" + } + */ + const result = await api.post(`/api/workflows:create`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 74, + "key": "il2nu3ovj53", + "updatedAt": "2023-12-12T06:53:21.232Z", + "createdAt": "2023-12-12T06:53:21.232Z", + "title": "t3", + "type": "collection", + "enabled": false, + "description": null, + "executed": 0, + "allExecuted": 0, + "config": {}, + "current": true, + "options": { + "deleteExecutionOnStatus": [] + }, + "useTransaction": true + } + } + */ + return (await result.json()).data; +}; + +// 更新工作流 +export const apiUpdateWorkflow = async (id: number, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "id": 74, + "key": "il2nu3ovj53", + "updatedAt": "2023-12-12T06:53:21.232Z", + "createdAt": "2023-12-12T06:53:21.232Z", + "title": "t3", + "type": "collection", + "enabled": false, + "description": null, + "executed": 0, + "allExecuted": 0, + "current": true, + "options": { + "deleteExecutionOnStatus": [] + }, + "useTransaction": true + } + */ + const result = await api.post(`/api/workflows:update?filterByTk=${id}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /*{ + "data": [ + { + "id": 72, + "createdAt": "2023-12-12T02:43:53.793Z", + "updatedAt": "2023-12-12T05:41:33.300Z", + "key": "fzk3j2oj4el", + "title": "a11", + "enabled": true, + "description": null, + "type": "collection", + "config": {}, + "useTransaction": true, + "executed": 0, + "allExecuted": 0, + "current": true, + "options": { + "deleteExecutionOnStatus": [] + } + } + ] + }*/ + return (await result.json()).data; +}; + +// 删除工作流 +export const apiDeleteWorkflow = async (id: number) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + + const result = await api.post(`/api/workflows:destroy?filterByTk=${id}`, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + // {"data":1} + return (await result.json()).data; +}; + +// 查询工作流 +export const apiGetWorkflow = async (id: number) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + + const result = await api.get(`/api/workflows:get?filterByTk=${id}`, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 73, + "createdAt": "2023-12-12T05:59:52.741Z", + "updatedAt": "2023-12-12T05:59:52.741Z", + "key": "1iuin0qchrh", + "title": "t2", + "enabled": false, + "description": null, + "type": "collection", + "config": { }, + "useTransaction": true, + "executed": 0, + "allExecuted": 0, + "current": true, + "options": { } + } + } + */ + return (await result.json()).data; +}; + +// 更新工作流触发器节点 +export const apiUpdateWorkflowTrigger = async (id: number, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "config": { + "appends": [ + "dept" + ], + "collection": "tt_mnt_org", + "changed": [], + "condition": { + "$and": [ + { + "id": { + "$eq": 1 + } + } + ] + }, + "mode": 1 + } + } + */ + const result = await api.post(`/api/workflows:update?filterByTk=${id}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": [ + { + "id": 73, + "createdAt": "2023-12-12T05:59:52.741Z", + "updatedAt": "2023-12-12T06:13:36.068Z", + "key": "1iuin0qchrh", + "title": "t2", + "enabled": false, + "description": null, + "type": "collection", + "config": { + "appends": [ + "dept" + ], + "collection": "tt_mnt_org", + "changed": [], + "condition": { + "$and": [ + { + "id": { + "$eq": 1 + } + } + ] + }, + "mode": 1 + }, + "useTransaction": true, + "executed": 0, + "allExecuted": 0, + "current": true, + "options": {} + } + ] + } + */ + return (await result.json()).data; +}; + +// 添加工作流节点 +export const apiCreateWorkflowNode = async (workflowId: number, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + /*upstreamId前置节点id ,null代表再触发器节点后面增加节点 + { + "type": "calculation", + "upstreamId": 263, + "branchIndex": null, + "title": "运算", + "config": {} + } + */ + const result = await api.post(`/api/workflows/${workflowId}/nodes:create`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 265, + "type": "calculation", + "upstreamId": null, + "branchIndex": null, + "title": "运算", + "config": {}, + "workflowId": 76, + "updatedAt": "2023-12-16T10:56:39.288Z", + "createdAt": "2023-12-16T10:56:39.281Z", + "key": "20jz2urt5w7", + "downstreamId": 263 + } + } + */ + return (await result.json()).data; +}; + +// 查询工作流节点 +export const apiGetWorkflowNode = async (id: number) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + + const result = await api.get(`/api/flow_nodes:get?filterByTk=${id}`, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 267, + "createdAt": "2023-12-17T06:56:10.147Z", + "updatedAt": "2023-12-17T08:53:38.117Z", + "key": "idr5wibhyqn", + "title": "查询数据", + "upstreamId": 269, + "branchIndex": null, + "downstreamId": 270, + "type": "query", + "config": { + "collection": "users", + "params": { + "filter": { + "$and": [ + { + "id": { + "$eq": "{{$context.data.id}}" + } + } + ] + }, + "sort": [], + "page": 1, + "pageSize": 20, + "appends": [] + } + }, + "workflowId": 76 + } + } + */ + return (await result.json()).data; +}; + +// 更新工作流节点配置 +export const apiUpdateWorkflowNode = async (id: number, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "config": { + "engine": "math.js", + "expression": "1" + } + } + */ + const result = await api.post(`/api/flow_nodes:update?filterByTk=${id}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": [ + { + "id": 266, + "createdAt": "2023-12-16T10:56:58.586Z", + "updatedAt": "2023-12-16T11:16:32.796Z", + "key": "atbyvcs5mwc", + "title": "运算", + "upstreamId": 263, + "branchIndex": null, + "downstreamId": null, + "type": "calculation", + "config": { + "engine": "math.js", + "expression": "1" + }, + "workflowId": 76 + } + ] + } + */ + return (await result.json()).data; +}; + +// 查询节点执行历史 +export const apiGetWorkflowNodeExecutions = async (id: number) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + + const state = await api.storageState(); + const headers = getHeaders(state); + const url = `/api/executions:list?appends[]=jobs&filter[workflowId]=${id}&fields=id,createdAt,updatedAt,key,status,workflowId,jobs`; + const result = await api.get(url, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": [ + { + "id": 15, + "createdAt": "2023-12-13T02:13:13.737Z", + "updatedAt": "2023-12-13T02:13:13.799Z", + "key": "yibkv8h2uq6", + "useTransaction": false, + "status": 1, + "workflowId": 16, + "jobs": [ + { + "id": 16, + "createdAt": "2023-12-13T02:13:13.783Z", + "updatedAt": "2023-12-13T02:13:13.783Z", + "executionId": 15, + "nodeId": 16, + "upstreamId": null, + "status": 1, + "result": false + }, + { + "id": 17, + "createdAt": "2023-12-13T02:13:13.788Z", + "updatedAt": "2023-12-13T02:13:13.788Z", + "executionId": 15, + "nodeId": 17, + "upstreamId": 16, + "status": 1, + "result": true + } + ] + }, + { + "id": 14, + "createdAt": "2023-12-13T02:09:00.529Z", + "updatedAt": "2023-12-13T02:09:00.590Z", + "key": "yibkv8h2uq6", + "useTransaction": false, + "status": 1, + "workflowId": 16, + "jobs": [ + { + "id": 14, + "createdAt": "2023-12-13T02:09:00.575Z", + "updatedAt": "2023-12-13T02:09:00.575Z", + "executionId": 14, + "nodeId": 16, + "upstreamId": null, + "status": 1, + "result": false + }, + { + "id": 15, + "createdAt": "2023-12-13T02:09:00.583Z", + "updatedAt": "2023-12-13T02:09:00.583Z", + "executionId": 14, + "nodeId": 17, + "upstreamId": 14, + "status": 1, + "result": true + } + ] + } + ], + "meta": { + "count": 2, + "page": 1, + "pageSize": 20, + "totalPage": 1 + } + } + */ + return (await result.json()).data; +}; + +// 更新业务表单条数据 +export const apiUpdateRecord = async (collectionName: string, id: number, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + const state = await api.storageState(); + const headers = getHeaders(state); + const result = await api.post(`/api/${collectionName}:update?filterByTk=${id}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": [ + { + "id": 1, + "createdAt": "2023-12-12T02:43:53.793Z", + "updatedAt": "2023-12-12T05:41:33.300Z", + "key": "fzk3j2oj4el", + "title": "a11", + "enabled": true, + "description": null + } + ] + } + */ + return (await result.json()).data; +}; + +// 查询业务表单条数据 +export const apiGetRecord = async (collectionName: string, id: number) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + const state = await api.storageState(); + const headers = getHeaders(state); + const result = await api.get(`/api/${collectionName}:get?filterByTk=${id}`, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 1, + "createdAt": "2023-12-12T02:43:53.793Z", + "updatedAt": "2023-12-12T05:41:33.300Z", + "key": "fzk3j2oj4el", + "title": "a11", + "enabled": true, + "description": null + }, + "meta": { + "allowedActions": { + "view": [ + 1 + ], + "update": [ + 1 + ], + "destroy": [ + 1 + ] + } + } + } + */ + return (await result.json()).data; +}; + +// 查询业务表list +export const apiGetList = async (collectionName: string) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + const state = await api.storageState(); + const headers = getHeaders(state); + const result = await api.get(`/api/${collectionName}:list`, { + headers, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": [ + { + "id": 1, + "createdAt": "2023-12-12T02:43:53.793Z", + "updatedAt": "2023-12-12T05:41:33.300Z", + "key": "fzk3j2oj4el", + "title": "a11", + "enabled": true, + "description": null + } + ], + "meta": { + "count": 1, + "page": 1, + "pageSize": 20, + "totalPage": 1 + } + } + */ + return await result.json(); +}; + +// 添加业务表单条数据触发工作流表单事件,triggerWorkflows=key1!field,key2,key3!field.subfield +export const apiCreateRecordTriggerFormEvent = async (collectionName: string, triggerWorkflows: string, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "title": "a11", + "enabled": true, + "description": null + } + */ + const result = await api.post(`/api/${collectionName}:create?triggerWorkflows=${triggerWorkflows}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + { + "data": { + "id": 1, + "createdAt": "2023-12-12T02:43:53.793Z", + "updatedAt": "2023-12-12T05:41:33.300Z", + "key": "fzk3j2oj4el", + "title": "a11", + "enabled": true, + "description": null + }, + "meta": { + "allowedActions": { + "view": [ + 1 + ], + "update": [ + 1 + ], + "destroy": [ + 1 + ] + } + } + } + */ + return (await result.json()).data; +}; + +// 提交至工作流触发工作流表单事件 +export const apiSubmitRecordTriggerFormEvent = async (triggerWorkflows: string, data: any) => { + const api = await request.newContext({ + storageState: process.env.PLAYWRIGHT_AUTH_FILE, + }); + const state = await api.storageState(); + const headers = getHeaders(state); + /* + { + "title": "a11", + "enabled": true, + "description": null + } + */ + const result = await api.post(`/api/workflows:trigger?triggerWorkflows=${triggerWorkflows}`, { + headers, + data, + }); + + if (!result.ok()) { + throw new Error(await result.text()); + } + /* + {} + */ + return await result.json(); +}; + +const getStorageItem = (key: string, storageState: any) => { + return storageState.origins + .find((item) => item.origin === APP_BASE_URL) + ?.localStorage.find((item) => item.name === key)?.value; +}; + +function getHeaders(storageState: any) { + const headers: any = {}; + const token = getStorageItem('NOCOBASE_TOKEN', storageState); + const auth = getStorageItem('NOCOBASE_AUTH', storageState); + const subAppName = new URL(APP_BASE_URL).pathname.match(/^\/apps\/([^/]*)\/*/)?.[1]; + const hostName = new URL(APP_BASE_URL).host; + const locale = getStorageItem('NOCOBASE_LOCALE', storageState); + const timezone = '+08:00'; + const withAclMeta = 'true'; + const role = getStorageItem('NOCOBASE_ROLE', storageState); + + if (token) { + headers.Authorization = `Bearer ${token}`; + } + if (auth) { + headers['X-Authenticator'] = auth; + } + if (subAppName) { + headers['X-App'] = subAppName; + } + if (hostName) { + headers['X-Hostname'] = hostName; + } + if (locale) { + headers['X-Locale'] = locale; + } + if (timezone) { + headers['X-Timezone'] = timezone; + } + if (withAclMeta) { + headers['X-With-Acl-Meta'] = withAclMeta; + } + if (role) { + headers['X-Role'] = role; + } + + return headers; +} + +export default module.exports = { + apiCreateWorkflow, + apiUpdateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateWorkflowTrigger, + apiGetWorkflowNodeExecutions, + apiCreateWorkflowNode, + apiUpdateWorkflowNode, + apiGetWorkflowNode, + apiUpdateRecord, + apiGetRecord, + apiGetList, + apiCreateRecordTriggerFormEvent, + apiSubmitRecordTriggerFormEvent, +}; diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/index.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/index.ts new file mode 100644 index 000000000..726e40a73 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/index.ts @@ -0,0 +1,4 @@ +export * from './e2eCollectionModel'; +export * from './e2ePageObjectModel'; +export { default } from './e2ePageObjectModel'; +export * from './e2eUtils'; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/configuration.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/configuration.test.ts new file mode 100644 index 000000000..7642497da --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/configuration.test.ts @@ -0,0 +1,360 @@ +import { faker } from '@faker-js/faker'; +import { + CreateWorkFlow, + EditWorkFlow, + ScheduleTriggerNode, + WorkflowListRecords, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test.describe('Configuration page to configure the Trigger node', () => { + test('Triggered one minute after the current time of the customized time', async ({ + page, + mockCollections, + mockRecords, + }) => { + test.setTimeout(120000); + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const scheduleTriggerNode = new ScheduleTriggerNode(page, workFlowName, triggerNodeCollectionName); + await scheduleTriggerNode.nodeConfigure.click(); + await scheduleTriggerNode.startTimeEntryBox.click(); + await scheduleTriggerNode.startTimeEntryBox.fill(dayjs().add(60, 'second').format('YYYY-MM-DD HH:mm:ss')); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await scheduleTriggerNode.submitButton.click(); + + // 2、测试步骤:等待60秒 + await page.waitForTimeout(60000); + + // 3、预期结果:工作流成功触发 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration Page Path Jump Workflow Management Page', () => { + test('Schedule event Workflow Configuration Page Path Jump Workflow Management Page', async ({ page }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + // const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + // const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + // const triggerNodeFieldName = 'orgname'; + // const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + // await mockCollections(appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText).collections); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + + // 2、测试步骤:跳转到工作流管理页面 + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Schedule event Workflow History Version Configuration Page Path Jump Workflow Management Page', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + const now = new Date(); + now.setMinutes(now.getMinutes() + 1); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const timeWithTimeZone = now.toLocaleString('en-US', { timeZone: timeZone }); + const triggerNodeData = { config: { mode: 0, startsOn: timeWithTimeZone } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:等待60秒 + await page.waitForTimeout(60000); + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByLabel('more').click(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + //元素重复 + await page.getByLabel('version', { exact: true }).click(); + await page.getByLabel('version-1').click(); + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Schedule event Workflow Execution Log Page Path Jump Workflow Management Page', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + const now = new Date(); + now.setMinutes(now.getMinutes() + 1); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const timeWithTimeZone = now.toLocaleString('en-US', { timeZone: timeZone }); + const triggerNodeData = { config: { mode: 0, startsOn: timeWithTimeZone } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:等待60秒 + await page.waitForTimeout(60000); + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + await page.getByRole('link', { name: 'Workflow', exact: true }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test.skip('Schedule event Workflow Execution Log Page Path Jump Execution Log Screen', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + const now = new Date(); + now.setMinutes(now.getMinutes() + 1); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const timeWithTimeZone = now.toLocaleString('en-US', { timeZone: timeZone }); + const triggerNodeData = { config: { mode: 0, startsOn: timeWithTimeZone } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:等待60秒 + await page.waitForTimeout(60000); + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + //跳转其他执行日志界面,元素无法定位 + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + // expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page version switching', () => {}); + +test.describe('Configuration page disable enable', () => {}); + +test.describe('Configuration page execution history', () => {}); + +test.describe('Configuration page copy to new version', () => { + test('Copy the Schedule event of the Configuration Trigger node', async ({ page, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + const now = new Date(); + now.setMinutes(now.getMinutes() + 1); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const timeWithTimeZone = now.toLocaleString('en-US', { timeZone: timeZone }); + const triggerNodeData = { config: { mode: 0, startsOn: timeWithTimeZone } }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:等待60秒 + await page.waitForTimeout(60000); + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + + await workflowListRecords.configureAction.click(); + await page.getByLabel('more').click(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + const scheduleTriggerNode = new ScheduleTriggerNode(page, workFlowName, triggerNodeCollectionName); + await scheduleTriggerNode.nodeConfigure.click(); + // 3、预期结果:startTime时间为60秒后的时间 + const timeWithoutTimeZonea = now.toLocaleString('en-US', { timeZone: timeZone, hour12: false }); + // 将 MM/DD/YYYY, HH:mm:ss 格式的字符串转换为 YYYY-MM-DD HH:mm:ss 格式 + const parts = timeWithoutTimeZonea.split(/[\s,/:]+/); + const formattedTimeWithTimeZone = `${parts[2]}-${parts[0].padStart(2, '0')}-${parts[1].padStart(2, '0')} ${ + parts[3] + }:${parts[4]}:${parts[5]}`; + + await expect(scheduleTriggerNode.startTimeEntryBox).toHaveValue(formattedTimeWithTimeZone); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page delete version', () => {}); + +test.describe('Node Add Modify Delete', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/workflowCRUD.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/workflowCRUD.test.ts new file mode 100644 index 000000000..261300963 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/ScheduleEventTrigger/workflowCRUD.test.ts @@ -0,0 +1,174 @@ +import { faker } from '@faker-js/faker'; +import { + CreateWorkFlow, + EditWorkFlow, + ScheduleTriggerNode, + WorkflowListRecords, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test.describe('Filter', () => { + test('filter workflow name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、筛选工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit', exact: true }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Add new', () => { + test('add new Schedule event', async ({ page }) => { + // 添加工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Action-Add new-workflows').click(); + const createWorkFlow = new CreateWorkFlow(page); + const workFlowName = faker.string.alphanumeric(5); + await createWorkFlow.name.fill(workFlowName); + await createWorkFlow.triggerType.click(); + await page.getByRole('option', { name: 'Schedule event' }).click(); + await page.getByLabel('action-Action-Submit-workflows').click(); + + // 3、预期结果:列表中出现新建的工作流 + await expect(page.getByText(workFlowName)).toBeVisible(); + + // 4、后置处理:删除工作流 + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit', exact: true }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(workFlowName)).toBeHidden(); + }); +}); + +test.describe('Sync', () => {}); + +test.describe('Delete', () => { + test('delete Schedule event', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 删除工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit', exact: true }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeHidden(); + + // 4、后置处理:删除工作流 + }); +}); + +test.describe('Edit', () => { + test('edit Schedule event name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + let workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 编辑工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`).click(); + const editWorkFlow = new EditWorkFlow(page, workFlowName); + workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + await editWorkFlow.name.fill(workFlowName); + await page.getByLabel('action-Action-Submit-workflows').click(); + await page.waitForLoadState('networkidle'); + // 3、预期结果:编辑成功,列表中出现编辑后的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Duplicate', () => { + test('Duplicate Schedule event triggers with only unconfigured trigger nodes', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'schedule', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、复制工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Duplicate-workflows-${workFlowName}`).click(); + await page.getByLabel(`action-Action-Submit-workflows-${workFlowName}`).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(`${workFlowName} copy`)).toBeAttached(); + + // 4、后置处理:删除工作流 + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName} copy`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(`${workFlowName} copy`)).toBeHidden(); + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Executed', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/formulaEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/formulaEngine.test.ts new file mode 100644 index 000000000..557874253 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/formulaEngine.test.ts @@ -0,0 +1,761 @@ +import { faker } from '@faker-js/faker'; +import { + AggregateNode, + ClculationNode, + CollectionTriggerNode, + CreateRecordNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowNode, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, get trigger node single line text variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByText('Trigger variables').click(); + await page.getByText('Trigger data').click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('textbox')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data triggers, get predecessor Formula engine arithmetic node data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置计算节点 + const preCalculationNodeTitle = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preCalculationNodeData = { + type: 'calculation', + upstreamId: null, + branchIndex: null, + title: preCalculationNodeTitle, + config: { engine: 'formula.js', expression: '{{$context.data.orgname}}' }, + }; + const preCalculationNode = await apiCreateWorkflowNode(workflowId, preCalculationNodeData); + const preCalculationNodeObj = JSON.parse(JSON.stringify(preCalculationNode)); + const preCalculationNodeId = preCalculationNodeObj.id; + const getPreCalculationNode = await apiGetWorkflowNode(preCalculationNodeId); + const preCalculationNodeKey = getPreCalculationNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preCalculationNodePom = new ClculationNode(page, preCalculationNodeTitle); + await preCalculationNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByText('Node result').click(); + await page.getByRole('menuitemcheckbox', { name: preCalculationNodeTitle }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preCalculationNodeTitle}`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection events add data triggers, get single line of text data for front query data nodes', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preQueryRecordNodeTitle} / 公司名称(单行文本)`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, get front added data node single line text data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建新增数据节点数据表 + const createRecordNodeAppendText = faker.string.alphanumeric(6); + const createRecordNodeCollectionDisplayName = `自动>组织[普通表]${createRecordNodeAppendText}`; + const createRecordNodeCollectionName = `tt_amt_org${createRecordNodeAppendText}`; + const createRecordNodeFieldName = 'orgname'; + const createRecordNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createRecordNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置添加数据节点 + const preCreateRecordNodeTitle = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preCreateRecordNodeData = { + type: 'create', + upstreamId: null, + branchIndex: null, + title: preCreateRecordNodeTitle, + config: { + params: { values: { orgname: '{{$context.data.orgname}}' }, appends: [] }, + collection: createRecordNodeCollectionName, + }, + }; + const preCreateRecordNode = await apiCreateWorkflowNode(workflowId, preCreateRecordNodeData); + const preCreateRecordNodeObj = JSON.parse(JSON.stringify(preCreateRecordNode)); + const preCreateRecordNodeId = preCreateRecordNodeObj.id; + const getPreCreateRecordNode = await apiGetWorkflowNode(preCreateRecordNodeId); + const preCreateRecordNodeKey = getPreCreateRecordNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preCreateRecordNodePom = new CreateRecordNode(page, preCreateRecordNodeTitle); + await preCreateRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preCreateRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preCreateRecordNodeTitle} / 公司名称(单行文本)`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event adds data triggers, fetches data from front-end aggregation query nodes', async ({ + page, + mockCollections, + mockRecords, +}) => { + //创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置聚合查询节点 + const preAggregateNodeTitle = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preAggregateNodeData = { + type: 'aggregate', + upstreamId: null, + branchIndex: null, + title: preAggregateNodeTitle, + config: { + aggregator: 'count', + associated: false, + params: { field: 'id', filter: { $and: [] } }, + collection: triggerNodeCollectionName, + }, + }; + const preAggregateNode = await apiCreateWorkflowNode(workflowId, preAggregateNodeData); + const preAggregateNodeObj = JSON.parse(JSON.stringify(preAggregateNode)); + const preAggregateNodeId = preAggregateNodeObj.id; + const getPreAggregateNode = await apiGetWorkflowNode(preAggregateNodeId); + const preAggregateNodeKey = getPreAggregateNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preAggregateNodePom = new AggregateNode(page, preAggregateNodeTitle); + await preAggregateNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preAggregateNodeTitle }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preAggregateNodeTitle}`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(1); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test.skip('Collection event add data trigger, get front manual node add form single line text data', async ({ + page, + mockCollections, + mockRecords, +}) => { + // 创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建人工节点数据表 + const manualNodeAppendText = faker.string.alphanumeric(5); + const manualNodeCollectionDisplayName = `自动>组织[普通表]${manualNodeAppendText}`; + const manualNodeCollectionName = `tt_amt_org${manualNodeAppendText}`; + const manualNodeFieldName = 'orgname'; + const manualNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), manualNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置人工节点新增数据表单 + const preManualNodeNodeTitle = 'Manual' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preManualNodeNodeCustomFormTitle = 'Custom form' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preManualNodeNodeData = { type: 'manual', upstreamId: null, branchIndex: null, title: 'Manual', config: {} }; + const preManualNodeNode = await apiCreateWorkflowNode(workflowId, preManualNodeNodeData); + const preManualNodeNodeObj = JSON.parse(JSON.stringify(preManualNodeNode)); + const preManualNodeNodeId = preManualNodeNodeObj.id; + const getPreManualNodeNode = await apiGetWorkflowNode(preManualNodeNodeId); + const preAggregateNodeKey = getPreManualNodeNode.key; + const preManualNodeNodeConfig = { + config: { + assignees: [1], + schema: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Manual", { ns: "workflow-manual" })}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'AddBlockButton', + name: 'grid', + properties: { + '59t231zx5vx': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '13do6fj5t73': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + krb1bo0p486: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'FormBlockProvider', + 'x-decorator-props': { + formType: 'create', + resource: 'tt_mnt_org', + collection: 'tt_mnt_org', + }, + 'x-designer': 'CreateFormDesigner', + 'x-component': 'CardItem', + 'x-component-props': { + title: 'Create record4567', + }, + properties: { + kzqri0ib7ji: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'FormItemInitializers', + name: 'grid', + properties: { + yt2rbpepdsl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + eptw1hy20lm: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + orgname: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + name: 'orgname', + 'x-designer': 'FormItem.Designer', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': `${manualNodeCollectionName}.${manualNodeFieldName}`, + 'x-component-props': {}, + }, + }, + name: 'eptw1hy20lm', + }, + }, + name: 'yt2rbpepdsl', + }, + }, + }, + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'AddActionButton', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + marginTop: '1.5em', + flexWrap: 'wrap', + }, + }, + properties: { + resolve: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Continue the process", { ns: "workflow-manual" })}}', + 'x-decorator': 'ManualActionStatusProvider', + 'x-decorator-props': { + value: 1, + }, + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ useSubmit }}', + }, + 'x-designer': 'ManualActionDesigner', + 'x-designer-props': {}, + name: 'resolve', + }, + }, + 'x-decorator': 'ActionBarProvider', + name: 'actions', + }, + }, + name: 'kzqri0ib7ji', + }, + }, + name: 'krb1bo0p486', + }, + }, + name: '13do6fj5t73', + }, + }, + name: '59t231zx5vx', + }, + }, + }, + }, + name: 'tab1', + }, + }, + forms: { + kzqri0ib7ji: { + type: 'create', + title: preManualNodeNodeCustomFormTitle, + actions: [ + { + status: 1, + key: 'resolve', + }, + ], + collection: manualNodeCollectionName, + }, + }, + }, + }; + await apiUpdateWorkflowNode(preManualNodeNodeId, preManualNodeNodeConfig); + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preManualNodeNodePom = new AggregateNode(page, preManualNodeNodeTitle); + await preManualNodeNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await calculationNode.formulaCalculationEngine.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preManualNodeNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: preManualNodeNodeCustomFormTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Node result / ${preManualNodeNodeTitle} / ${preManualNodeNodeCustomFormTitle} / 公司名称(单行文本)`, + ); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // api请求办理人工节点 + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/mathEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/mathEngine.test.ts new file mode 100644 index 000000000..ff318ec9f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/calculationNode/mathEngine.test.ts @@ -0,0 +1,755 @@ +import { faker } from '@faker-js/faker'; +import { + AggregateNode, + ClculationNode, + CollectionTriggerNode, + CreateRecordNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowNode, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, get trigger node single line text variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByText('Trigger variables').click(); + await page.getByText('Trigger data').click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('textbox')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data triggers, get predecessor Math engine arithmetic node data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置计算节点 + const preCalculationNodeTitle = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preCalculationNodeData = { + type: 'calculation', + upstreamId: null, + branchIndex: null, + title: preCalculationNodeTitle, + config: { engine: 'math.js', expression: '{{$context.data.orgname}}' }, + }; + const preCalculationNode = await apiCreateWorkflowNode(workflowId, preCalculationNodeData); + const preCalculationNodeObj = JSON.parse(JSON.stringify(preCalculationNode)); + const preCalculationNodeId = preCalculationNodeObj.id; + const getPreCalculationNode = await apiGetWorkflowNode(preCalculationNodeId); + const preCalculationNodeKey = getPreCalculationNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preCalculationNodePom = new ClculationNode(page, preCalculationNodeTitle); + await preCalculationNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByText('Node result').click(); + await page.getByRole('menuitemcheckbox', { name: preCalculationNodeTitle }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preCalculationNodeTitle}`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection events add data triggers, get single line of text data for front query data nodes', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preQueryRecordNodeTitle} / 公司名称(单行文本)`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, get front added data node single line text data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建新增数据节点数据表 + const createRecordNodeAppendText = faker.string.alphanumeric(6); + const createRecordNodeCollectionDisplayName = `自动>组织[普通表]${createRecordNodeAppendText}`; + const createRecordNodeCollectionName = `tt_amt_org${createRecordNodeAppendText}`; + const createRecordNodeFieldName = 'orgname'; + const createRecordNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createRecordNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置添加数据节点 + const preCreateRecordNodeTitle = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preCreateRecordNodeData = { + type: 'create', + upstreamId: null, + branchIndex: null, + title: preCreateRecordNodeTitle, + config: { + params: { values: { orgname: '{{$context.data.orgname}}' }, appends: [] }, + collection: createRecordNodeCollectionName, + }, + }; + const preCreateRecordNode = await apiCreateWorkflowNode(workflowId, preCreateRecordNodeData); + const preCreateRecordNodeObj = JSON.parse(JSON.stringify(preCreateRecordNode)); + const preCreateRecordNodeId = preCreateRecordNodeObj.id; + const getPreCreateRecordNode = await apiGetWorkflowNode(preCreateRecordNodeId); + const preCreateRecordNodeKey = getPreCreateRecordNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preCreateRecordNodePom = new CreateRecordNode(page, preCreateRecordNodeTitle); + await preCreateRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preCreateRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preCreateRecordNodeTitle} / 公司名称(单行文本)`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event adds data triggers, fetches data from front-end aggregation query nodes', async ({ + page, + mockCollections, + mockRecords, +}) => { + //创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置聚合查询节点 + const preAggregateNodeTitle = 'Aggregate' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preAggregateNodeData = { + type: 'aggregate', + upstreamId: null, + branchIndex: null, + title: preAggregateNodeTitle, + config: { + aggregator: 'count', + associated: false, + params: { field: 'id', filter: { $and: [] } }, + collection: triggerNodeCollectionName, + }, + }; + const preAggregateNode = await apiCreateWorkflowNode(workflowId, preAggregateNodeData); + const preAggregateNodeObj = JSON.parse(JSON.stringify(preAggregateNode)); + const preAggregateNodeId = preAggregateNodeObj.id; + const getPreAggregateNode = await apiGetWorkflowNode(preAggregateNodeId); + const preAggregateNodeKey = getPreAggregateNode.key; + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preAggregateNodePom = new AggregateNode(page, preAggregateNodeTitle); + await preAggregateNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preAggregateNodeTitle }).click(); + await expect(page.getByLabel('textbox')).toHaveText(`Node result / ${preAggregateNodeTitle}`); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(1); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test.skip('Collection event add data trigger, get front manual node add form single line text data', async ({ + page, + mockCollections, + mockRecords, +}) => { + // 创建触发器节点数据表 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建人工节点数据表 + const manualNodeAppendText = faker.string.alphanumeric(5); + const manualNodeCollectionDisplayName = `自动>组织[普通表]${manualNodeAppendText}`; + const manualNodeCollectionName = `tt_amt_org${manualNodeAppendText}`; + const manualNodeFieldName = 'orgname'; + const manualNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), manualNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置人工节点新增数据表单 + const preManualNodeNodeTitle = 'Manual' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preManualNodeNodeCustomFormTitle = 'Custom form' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preManualNodeNodeData = { type: 'manual', upstreamId: null, branchIndex: null, title: 'Manual', config: {} }; + const preManualNodeNode = await apiCreateWorkflowNode(workflowId, preManualNodeNodeData); + const preManualNodeNodeObj = JSON.parse(JSON.stringify(preManualNodeNode)); + const preManualNodeNodeId = preManualNodeNodeObj.id; + const getPreManualNodeNode = await apiGetWorkflowNode(preManualNodeNodeId); + const preAggregateNodeKey = getPreManualNodeNode.key; + const preManualNodeNodeConfig = { + config: { + assignees: [1], + schema: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Manual", { ns: "workflow-manual" })}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'AddBlockButton', + name: 'grid', + properties: { + '59t231zx5vx': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '13do6fj5t73': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + krb1bo0p486: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'FormBlockProvider', + 'x-decorator-props': { + formType: 'create', + resource: 'tt_mnt_org', + collection: 'tt_mnt_org', + }, + 'x-designer': 'CreateFormDesigner', + 'x-component': 'CardItem', + 'x-component-props': { + title: 'Create record4567', + }, + properties: { + kzqri0ib7ji: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'FormItemInitializers', + name: 'grid', + properties: { + yt2rbpepdsl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + eptw1hy20lm: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + orgname: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + name: 'orgname', + 'x-designer': 'FormItem.Designer', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': `${manualNodeCollectionName}.${manualNodeFieldName}`, + 'x-component-props': {}, + }, + }, + name: 'eptw1hy20lm', + }, + }, + name: 'yt2rbpepdsl', + }, + }, + }, + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'AddActionButton', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + marginTop: '1.5em', + flexWrap: 'wrap', + }, + }, + properties: { + resolve: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Continue the process", { ns: "workflow-manual" })}}', + 'x-decorator': 'ManualActionStatusProvider', + 'x-decorator-props': { + value: 1, + }, + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ useSubmit }}', + }, + 'x-designer': 'ManualActionDesigner', + 'x-designer-props': {}, + name: 'resolve', + }, + }, + 'x-decorator': 'ActionBarProvider', + name: 'actions', + }, + }, + name: 'kzqri0ib7ji', + }, + }, + name: 'krb1bo0p486', + }, + }, + name: '13do6fj5t73', + }, + }, + name: '59t231zx5vx', + }, + }, + }, + }, + name: 'tab1', + }, + }, + forms: { + kzqri0ib7ji: { + type: 'create', + title: preManualNodeNodeCustomFormTitle, + actions: [ + { + status: 1, + key: 'resolve', + }, + ], + collection: manualNodeCollectionName, + }, + }, + }, + }; + await apiUpdateWorkflowNode(preManualNodeNodeId, preManualNodeNodeConfig); + + //配置计算节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preManualNodeNodePom = new AggregateNode(page, preManualNodeNodeTitle); + await preManualNodeNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const calculationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Calculation-Calculation', { exact: true }).getByRole('textbox').fill(calculationNodeName); + const calculationNode = new ClculationNode(page, calculationNodeName); + const conditionNodeId = await calculationNode.node.locator('.workflow-node-id').innerText(); + await calculationNode.nodeConfigure.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preManualNodeNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: preManualNodeNodeCustomFormTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司名称(单行文本)' }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Node result / ${preManualNodeNodeTitle} / ${preManualNodeNodeCustomFormTitle} / 公司名称(单行文本)`, + ); + await calculationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // api请求办理人工节点 + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/configuration.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/configuration.test.ts new file mode 100644 index 000000000..e544d178f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/configuration.test.ts @@ -0,0 +1,735 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + CreateWorkFlow, + EditWorkFlow, + WorkflowListRecords, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateRecord, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; + +test.describe('Configuration page to configure the Trigger node', () => { + test('Add Data Trigger with No Filter', async ({ page, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await collectionTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await collectionTriggerNode.triggerOnDropdown.click(); + await page.getByText('After record added', { exact: true }).click(); + await collectionTriggerNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Modify Data Without Filter Trigger', async ({ page, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await collectionTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await collectionTriggerNode.triggerOnDropdown.click(); + await page.getByText('After record updated', { exact: true }).click(); + await collectionTriggerNode.submitButton.click(); + + // 2、测试步骤:添加数据,编辑数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + const recordId = triggerNodeCollectionRecords[0].id; + await apiUpdateRecord(triggerNodeCollectionName, recordId, { orgname: triggerNodeCollectionRecordOne + '1' }); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('No-filter new or modified data triggers', async ({ page, mockCollections, mockRecords }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await collectionTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await collectionTriggerNode.triggerOnDropdown.click(); + await page.getByText('After record added or updated', { exact: true }).click(); + await collectionTriggerNode.submitButton.click(); + + // 2、测试步骤:添加数据,编辑数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + const recordId = triggerNodeCollectionRecords[0].id; + await apiUpdateRecord(triggerNodeCollectionName, recordId, { orgname: triggerNodeCollectionRecordOne + '1' }); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(2); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Filter radio fields equal to a specific value new data trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await collectionTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await collectionTriggerNode.triggerOnDropdown.click(); + await page.getByText('After record added or updated', { exact: true }).click(); + // 设置触发器过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only triggers when match conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByText('公司状态(下拉单选)').click(); + await page + .getByLabel('block-item-Filter-workflows-Only triggers when match conditions') + .getByTestId('select-single') + .click(); + await page.getByRole('option', { name: '存续' }).click(); + await collectionTriggerNode.submitButton.click(); + + // 2、测试步骤:添加数据,编辑数据触发工作流 + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [{ status_singleselect: '1' }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Filter radio fields equal to a specific value Edit Data Trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await collectionTriggerNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await collectionTriggerNode.triggerOnDropdown.click(); + await page.getByText('After record updated', { exact: true }).click(); + // 设置触发器过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only triggers when match conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByText('公司状态(下拉单选)').click(); + await page + .getByLabel('block-item-Filter-workflows-Only triggers when match conditions') + .getByTestId('select-single') + .click(); + await page.getByRole('option', { name: '存续' }).click(); + await collectionTriggerNode.submitButton.click(); + + // 2、测试步骤:添加数据,编辑数据触发工作流 + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [{ status_singleselect: '1' }]); + await page.waitForTimeout(1000); + const recordId = triggerNodeCollectionRecords[0].id; + await apiUpdateRecord(triggerNodeCollectionName, recordId, { orgname: '1' }); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration Page Path Jump Workflow Management Page', () => { + test('Collection event Workflow Configuration Page Path Jump Workflow Management Page', async ({ + page, + mockCollections, + mockRecords, + }) => { + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + // const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + // const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + // const triggerNodeFieldName = 'orgname'; + // const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + // await mockCollections(appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText).collections); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + //配置工作流触发器 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + + // 2、测试步骤:跳转到工作流管理页面 + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Collection event Workflow History Version Configuration Page Path Jump Workflow Management Page', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByLabel('more').click(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + //元素重复 + await page.getByLabel('version', { exact: true }).click(); + await page.getByLabel('version-1').click(); + await page.getByRole('link', { name: 'Workflow' }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Collection event Workflow Execution Log Page Path Jump Workflow Management Page', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + await page.getByRole('link', { name: 'Workflow', exact: true }).click(); + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test.skip('Collection event Workflow Execution Log Page Path Jump Execution Log Screen', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + const workflowListRecords = new WorkflowListRecords(page, workFlowName); + await expect(workflowListRecords.executionCountPopup).toHaveText('1'); + await workflowListRecords.executionCountPopup.click(); + await page.getByText('View').click(); + await page.waitForLoadState('networkidle'); + //跳转其他执行日志界面,元素无法定位 + + // 3、预期结果:跳转路径正确 + await page.waitForLoadState('networkidle'); + // expect(page.url()).toBe(`${process.env.APP_BASE_URL}/admin/settings/workflow`); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page version switching', () => {}); + +test.describe('Configuration page disable enable', () => { + test('Collection Event Workflow Add Data Trigger Disable Do Not Trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:触发次数为1 + let getWorkflow = await apiGetWorkflow(workflowId); + let getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + let getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByRole('switch', { name: 'On Off' }).click(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne + '1' }]); + await page.waitForTimeout(1000); + + getWorkflow = await apiGetWorkflow(workflowId); + getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); + + test('Collection Event Workflow Add Data Trigger Disable Enable Post Trigger', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: false, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:触发次数为1 + let getWorkflow = await apiGetWorkflow(workflowId); + let getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + let getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(0); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.getByRole('switch', { name: 'On Off' }).click(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne + '1' }]); + await page.waitForTimeout(1000); + + getWorkflow = await apiGetWorkflow(workflowId); + getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page execution history', () => {}); + +test.describe('Configuration page copy to new version', () => { + test('Copy the Collection event of the Configuration Trigger node', async ({ + page, + mockCollections, + mockRecords, + }) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + await page.locator('.workflow-toolbar').getByLabel('more').hover(); + await page.getByLabel('revision').click(); + await page.waitForLoadState('networkidle'); + // 3、预期结果:新版本工作流配置内容同旧版本一样 + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.nodeConfigure.click(); + await expect(page.getByRole('button', { name: triggerNodeCollectionDisplayName })).toBeVisible(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Configuration page delete version', () => {}); + +test.describe('Node Add Modify Delete', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/workflowCRUD.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/workflowCRUD.test.ts new file mode 100644 index 000000000..4f70b8680 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/collectionEventTrigger/workflowCRUD.test.ts @@ -0,0 +1,174 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + CreateWorkFlow, + EditWorkFlow, + WorkflowListRecords, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiUpdateRecord, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; + +test.describe('Filter', () => { + test('filter workflow name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、筛选工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Add new', () => { + test('add new Collection event', async ({ page }) => { + // 添加工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Action-Add new-workflows').click(); + const createWorkFlow = new CreateWorkFlow(page); + const workFlowName = faker.string.alphanumeric(5); + await createWorkFlow.name.fill(workFlowName); + await createWorkFlow.triggerType.click(); + await page.getByRole('option', { name: 'Collection event' }).click(); + await page.getByLabel('action-Action-Submit-workflows').click(); + + // 3、预期结果:列表中出现新建的工作流 + await expect(page.getByText(workFlowName)).toBeVisible(); + + // 4、后置处理:删除工作流 + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(workFlowName)).toBeHidden(); + }); +}); + +test.describe('Sync', () => {}); + +test.describe('Delete', () => { + test('delete Collection event', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 删除工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); + await page.getByRole('textbox').fill(workFlowName); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(workFlowName)).toBeHidden(); + + // 4、后置处理:删除工作流 + }); +}); + +test.describe('Edit', () => { + test('edit Collection event name', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + let workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 编辑工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`).click(); + const editWorkFlow = new EditWorkFlow(page, workFlowName); + workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + await editWorkFlow.name.fill(workFlowName); + await page.getByLabel('action-Action-Submit-workflows').click(); + await page.waitForLoadState('networkidle'); + // 3、预期结果:编辑成功,列表中出现编辑后的工作流 + await expect(page.getByText(workFlowName)).toBeAttached(); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Duplicate', () => { + test('Duplicate Collection event triggers with only unconfigured trigger nodes', async ({ page }) => { + //添加工作流 + const triggerNodeAppendText = faker.string.alphanumeric(5); + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + + // 2、复制工作流 + await page.goto('/admin/settings/workflow'); + await page.waitForLoadState('networkidle'); + await page.getByLabel(`action-Action.Link-Duplicate-workflows-${workFlowName}`).click(); + await page.getByLabel(`action-Action-Submit-workflows-${workFlowName}`).click(); + + // 3、预期结果:列表中出现筛选的工作流 + await expect(page.getByText(`${workFlowName} copy`)).toBeAttached(); + + // 4、后置处理:删除工作流 + await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName} copy`).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect(page.getByText(`${workFlowName} copy`)).toBeHidden(); + await apiDeleteWorkflow(workflowId); + }); +}); + +test.describe('Executed', () => {}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoBasicType.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoBasicType.test.ts new file mode 100644 index 000000000..2d4cf1f7b --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoBasicType.test.ts @@ -0,0 +1,1061 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, determines that the trigger node single line text field variable is equal to an equal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('variable-tag')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determines that the trigger node single line text field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('variable-tag')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant + '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('variable-tag')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determines that the trigger node single line text field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText('公司名称(单行文本)').click(); + await expect(page.getByLabel('variable-tag')).toHaveText('Trigger variables / Trigger data / 公司名称(单行文本)'); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant + '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable equal to query node equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is equal to query node not equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is not equal to query node equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is not equal to query node not equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoFormulaEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoFormulaEngine.test.ts new file mode 100644 index 000000000..a7e8ca188 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoFormulaEngine.test.ts @@ -0,0 +1,1061 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is equal to an equal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.lorem.words(); + await page.keyboard.type(`=='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}=='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.lorem.words(); + await page.keyboard.type(`=='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}=='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant + '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.lorem.words(); + await page.keyboard.type(`!='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.lorem.words(); + await page.keyboard.type(`!='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant + '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine the trigger node integer variable is equal to the query node equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('=='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is equal to query node not equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('=='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is not equal to query node equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('!='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine the trigger node integer variable is not equal to the query node not equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.lorem.words(); + await page.keyboard.type(`!='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoMathEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoMathEngine.test.ts new file mode 100644 index 000000000..5f883094a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/BranchIntoYesAndNoMathEngine.test.ts @@ -0,0 +1,481 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, determine trigger node integer field variable is equal to an equal constant, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`==${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node integer field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`==${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node integer field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`!=${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = conditionalRightConstant; + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点false + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(false); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob.result).toBe(0); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob).toBe(undefined); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determines that the trigger node integer field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('branch').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionBranchNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`!=${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + //添加No分支计算节点 + await conditionNode.addNoBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const noBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(noBranchcalCulationNodeName); + const noBranchcalCulationNode = new ClculationNode(page, noBranchcalCulationNodeName); + const noBranchcalCulationNodeId = await noBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await noBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('0'); + await noBranchcalCulationNode.submitButton.click(); + //添加Yes分支计算节点 + await conditionNode.addYesBranchNode.click(); + await page.getByRole('button', { name: 'calculation', exact: true }).click(); + const yesBranchcalCulationNodeName = 'calculation' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('Calculation-Calculation', { exact: true }) + .getByRole('textbox') + .fill(yesBranchcalCulationNodeName); + const yesBranchcalCulationNode = new ClculationNode(page, yesBranchcalCulationNodeName); + const yesBranchcalCulationNodeId = await yesBranchcalCulationNode.node.locator('.workflow-node-id').innerText(); + await yesBranchcalCulationNode.nodeConfigure.click(); + await page.getByLabel('textbox').fill('1'); + await yesBranchcalCulationNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const conditionNodeJob = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(conditionNodeJob.result).toBe(true); + const noBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === noBranchcalCulationNodeId); + expect(noBranchcalCulationNodeJob).toBe(undefined); + const yesBranchcalCulationNodeJob = jobs.find((job) => job.nodeId.toString() === yesBranchcalCulationNodeId); + expect(yesBranchcalCulationNodeJob.result).toBe(1); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesBasicType.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesBasicType.test.ts new file mode 100644 index 000000000..8713a3b1b --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesBasicType.test.ts @@ -0,0 +1,812 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, determines that the trigger node single line text field variable is equal to an equal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determines that the trigger node single line text field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, determines that the trigger node single line text field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determines that the trigger node single line text field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByText(triggerNodeFieldDisplayName).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + const conditionalRightConstant = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('variable-constant').fill(conditionalRightConstant); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant + '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable equal to query node equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is equal to query node not equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is not equal to query node equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, determine trigger node integer variable is not equal to query node not equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').first().click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await page.getByLabel('select-operator-calc').first().click(); + await page.getByRole('option', { name: '≠' }).click(); + await page.getByLabel('variable-button').nth(1).click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('variable-tag').nth(1)).toHaveText( + `Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesFormulaEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesFormulaEngine.test.ts new file mode 100644 index 000000000..a53cad25d --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesFormulaEngine.test.ts @@ -0,0 +1,790 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event Add Data Trigger, Formula engine, determines that the trigger node single line text field variable is equal to an equal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.string.alphanumeric(10); + await page.keyboard.type(`=='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}=='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, Formula engine, determines that the trigger node single line text field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.string.alphanumeric(10); + await page.keyboard.type(`=='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}=='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, Formula engine, determines that the trigger node single line text field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.string.alphanumeric(10); + await page.keyboard.type(`!='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, Formula engine, determines that the trigger node single line text field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.string.alphanumeric(10); + await page.keyboard.type(`!='${conditionalRightConstant}'`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!='${conditionalRightConstant}'`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.string.alphanumeric(9); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, Formula engine, determine the trigger node integer variable is equal to the query node equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('=='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, Formula engine, determine trigger node integer variable is equal to query node not equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('=='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, Formula engine, determine trigger node integer variable is not equal to query node equal integer variable, not pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $eq: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('!='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, Formula engine, determine the trigger node integer variable is not equal to the query node not equal integer variable, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordOne = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordOne }]); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置前置查询节点 + const preQueryRecordNodeTitle = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const preQueryRecordNodeData = { + type: 'query', + upstreamId: null, + branchIndex: null, + title: preQueryRecordNodeTitle, + config: { + collection: triggerNodeCollectionName, + params: { + filter: { $and: [{ id: { $ne: '{{$context.data.id}}' } }] }, + sort: [], + page: 1, + pageSize: 20, + appends: [], + }, + }, + }; + const preQueryRecordNode = await apiCreateWorkflowNode(workflowId, preQueryRecordNodeData); + const preQueryRecordNodeObj = JSON.parse(JSON.stringify(preQueryRecordNode)); + const preQueryRecordNodeId = preQueryRecordNodeObj.id; + const getPreQueryRecordNode = await apiGetWorkflowNode(preQueryRecordNodeId); + const preQueryRecordNodeKey = getPreQueryRecordNode.key; + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const preQueryRecordNodePom = new QueryRecordNode(page, preQueryRecordNodeTitle); + await preQueryRecordNodePom.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.formulaRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + await page.keyboard.type('!='); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Node result' }).click(); + await page.getByRole('menuitemcheckbox', { name: preQueryRecordNodeTitle }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=Node result / ${preQueryRecordNodeTitle} / ${triggerNodeFieldDisplayName}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordTwo = faker.number.int(); + await mockRecords(triggerNodeCollectionName, [{ staffnum: triggerNodeCollectionRecordTwo }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const job = jobs.find((job) => job.nodeId.toString() === conditionNodeId); + expect(job.result).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesMathEngine.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesMathEngine.test.ts new file mode 100644 index 000000000..6bdb82075 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/conditionNode/continueWhenYesMathEngine.test.ts @@ -0,0 +1,354 @@ +import { faker } from '@faker-js/faker'; +import { + ClculationNode, + CollectionTriggerNode, + ConditionBranchNode, + ConditionYesNode, + QueryRecordNode, + apiCreateWorkflow, + apiCreateWorkflowNode, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNode, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, Math engine, determine trigger node integer field variable is equal to an equal constant, pass.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`==${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, Math engine, determines that the trigger node integer field variable is equal to an unequal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + const conditionNodeId = await conditionNode.node.locator('.workflow-node-id').innerText(); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`==${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}==${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event Add Data Trigger, Math engine, determines that the trigger node integer field variable is not equal to an equal constant, fails.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`!=${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = conditionalRightConstant; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(false); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, Math engine, determines that the trigger node integer field variable is not equal to an unequal constant, passes.', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置判断节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'condition', exact: true }).hover(); + await page.getByLabel('rejectOnFalse').click(); + const conditionNodeName = 'condition' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Condition-Condition', { exact: true }).getByRole('textbox').fill(conditionNodeName); + const conditionNode = new ConditionYesNode(page, conditionNodeName); + await conditionNode.nodeConfigure.click(); + await conditionNode.mathRadio.click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await page.getByLabel('textbox').focus(); + const conditionalRightConstant = faker.number.int(); + await page.keyboard.type(`!=${conditionalRightConstant}`); + await expect(page.getByLabel('textbox')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}!=${conditionalRightConstant}`, + ); + await conditionNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,判断节点true通过 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const getWorkflowNodeExecutionsObjResult = getWorkflowNodeExecutionsObj[0].jobs[0].result; + expect(getWorkflowNodeExecutionsObjResult).toBe(true); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/createRecordNode/CreateRecord.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/createRecordNode/CreateRecord.test.ts new file mode 100644 index 000000000..6c85b964e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/createRecordNode/CreateRecord.test.ts @@ -0,0 +1,1212 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + CreateRecordNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetRecord, + apiGetWorkflow, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, single row text fields for common tables, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'orgname'; + const createNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + const createRecordNodefieldData = createNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page + .getByLabel('block-item-CollectionFieldset-workflows-Fields values') + .getByRole('textbox') + .fill(createRecordNodefieldData); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.orgname).toBe(createRecordNodefieldData); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.orgname).toBe(createRecordNodefieldData); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table single line text field, set trigger node single line text field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'orgname'; + const createNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.orgname).toBe(triggerNodeCollectionRecordOne); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.orgname).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table integer field, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'staffnum'; + const createNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + const createRecordNodefieldData = faker.number.int(); + await page + .getByLabel('block-item-CollectionFieldset-workflows-Fields values') + .getByRole('spinbutton') + .fill(createRecordNodefieldData.toString()); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.staffnum).toBe(createRecordNodefieldData); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.staffnum).toBe(createRecordNodefieldData); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table integer field, set trigger node integer field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'staffnum'; + const createNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.staffnum).toBe(triggerNodeCollectionRecordOne); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.staffnum).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table numeric field, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'regcapital'; + const createNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + const createRecordNodefieldData = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + await page + .getByLabel('block-item-CollectionFieldset-workflows-Fields values') + .getByRole('spinbutton') + .fill(createRecordNodefieldData.toString()); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.regcapital).toBe(createRecordNodefieldData); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.regcapital).toBe(createRecordNodefieldData); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table numeric field, set trigger node numeric field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'regcapital'; + const triggerNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'regcapital'; + const createNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:录入数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { regcapital: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.regcapital).toBe(triggerNodeCollectionRecordOne); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.regcapital).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table dropdown radio field, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'status_singleselect'; + const createNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByTestId('select-single').click(); + await page.getByRole('option', { name: '存续' }).click(); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.status_singleselect).toBe('1'); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.status_singleselect).toBe('1'); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table dropdown radio field, set trigger node dropdown radio field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'status_singleselect'; + const triggerNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'status_singleselect'; + const createNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = '1'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { status_singleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.status_singleselect).toBe(triggerNodeCollectionRecordOne); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.status_singleselect).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table dropdown radio fields, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'range_multipleselect'; + const createNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByTestId('select-multiple').click(); + await page.getByRole('option', { name: '软件销售', exact: true }).click(); + await page.getByRole('option', { name: '软件开发', exact: true }).click(); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.range_multipleselect.toString()).toBe('F3134,I3006'); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.range_multipleselect.toString()).toBe('F3134,I3006'); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table dropdown radio fields, set trigger node dropdown radio field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'range_multipleselect'; + const triggerNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'range_multipleselect'; + const createNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = ['F3134', 'I3006']; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { range_multipleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(createRecordNodeJobResult.range_multipleselect.toString()).toBe(triggerNodeCollectionRecordOne.toString()); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(getRecordsObj.range_multipleselect.toString()).toBe(triggerNodeCollectionRecordOne.toString()); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table date field, set constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'establishdate'; + const createNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('block-item-CollectionFieldset').getByPlaceholder('Select date').click(); + const createRecordNodefieldData = dayjs().format('YYYY-MM-DD'); + await page + .getByLabel('block-item-CollectionFieldset') + .getByPlaceholder('Select date') + .fill(createRecordNodefieldData); + await page.getByTitle(createRecordNodefieldData.toString()).locator('div').click(); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(dayjs(createRecordNodeJobResult.establishdate).format('YYYY-MM-DD')).toBe(createRecordNodefieldData); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(dayjs(getRecordsObj.establishdate).format('YYYY-MM-DD')).toBe(createRecordNodefieldData); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, normal table date field, set trigger node date field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const createNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'establishdate'; + const triggerNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建新增数据节点数据表 + const createNodeCollectionDisplayName = `自动>组织[普通表]${createNodeAppendText}`; + const createNodeCollectionName = `tt_amt_org${createNodeAppendText}`; + const createNodeFieldName = 'establishdate'; + const createNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), createNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置新增数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'create', exact: true }).click(); + const createRecordNodeName = 'Create record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Create record-Create record', { exact: true }).getByRole('textbox').fill(createRecordNodeName); + const createRecordNode = new CreateRecordNode(page, createRecordNodeName); + const createRecordNodeId = await createRecordNode.node.locator('.workflow-node-id').innerText(); + await createRecordNode.nodeConfigure.click(); + await createRecordNode.collectionDropDown.click(); + await page.getByText(createNodeCollectionDisplayName).click(); + // 设置字段 + await createRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: createNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await createRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = dayjs().add(1, 'day').format('YYYY-MM-DD'); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { establishdate: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据表成功新增记录 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const createRecordNodeJob = jobs.find((job) => job.nodeId.toString() === createRecordNodeId); + const createRecordNodeJobResult = createRecordNodeJob.result; + const createRecordId = createRecordNodeJobResult.id; + expect(dayjs(createRecordNodeJobResult.establishdate).format('YYYY-MM-DD')).toBe(triggerNodeCollectionRecordOne); + const getRecords = await apiGetRecord(createNodeCollectionName, createRecordId); + const getRecordsObj = JSON.parse(JSON.stringify(getRecords)); + expect(dayjs(getRecordsObj.establishdate).format('YYYY-MM-DD')).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/deleteRecordNode/DeleteRecord.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/deleteRecordNode/DeleteRecord.test.ts new file mode 100644 index 000000000..e40cb2755 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/deleteRecordNode/DeleteRecord.test.ts @@ -0,0 +1,207 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + DeleteRecordNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetList, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, filter single line text field not null, delete common table data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const deleteNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建删除数据节点数据表 + const deleteNodeCollectionDisplayName = `自动>组织[普通表]${deleteNodeAppendText}`; + const deleteNodeCollectionName = `tt_amt_org${deleteNodeAppendText}`; + const deleteNodeFieldName = 'orgname'; + const deleteNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), deleteNodeAppendText) + .collections, + ); + const deleteNodeCollectionRecordOne = 'a' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecordTwo = 'b' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecordThree = 'c' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecords = await mockRecords(deleteNodeCollectionName, [ + { orgname: deleteNodeCollectionRecordOne }, + { orgname: deleteNodeCollectionRecordTwo }, + { orgname: deleteNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置删除数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'destroy', exact: true }).click(); + const deleteRecordNodeName = 'Delete record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Delete record-Delete record', { exact: true }).getByRole('textbox').fill(deleteRecordNodeName); + const deleteRecordNode = new DeleteRecordNode(page, deleteRecordNodeName); + await deleteRecordNode.nodeConfigure.click(); + await deleteRecordNode.collectionDropDown.click(); + await page.getByText(deleteNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: deleteNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + await deleteRecordNode.submitButton.click(); + // 查看删除前数据 + let deleteNodeCollectionData = await apiGetList(deleteNodeCollectionName); + let deleteNodeCollectionDataObj = JSON.parse(JSON.stringify(deleteNodeCollectionData)); + expect(deleteNodeCollectionDataObj.meta.count).toBe(3); + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据删除成功 + deleteNodeCollectionData = await apiGetList(deleteNodeCollectionName); + deleteNodeCollectionDataObj = JSON.parse(JSON.stringify(deleteNodeCollectionData)); + expect(deleteNodeCollectionDataObj.meta.count).toBe(0); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter single line text field is trigger node single line text field variable , delete common table data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const deleteNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建删除数据节点数据表 + const deleteNodeCollectionDisplayName = `自动>组织[普通表]${deleteNodeAppendText}`; + const deleteNodeCollectionName = `tt_amt_org${deleteNodeAppendText}`; + const deleteNodeFieldName = 'orgname'; + const deleteNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), deleteNodeAppendText) + .collections, + ); + const deleteNodeCollectionRecordOne = 'a' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecordTwo = 'b' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecordThree = 'c' + faker.string.alphanumeric(4); + const deleteNodeCollectionRecords = await mockRecords(deleteNodeCollectionName, [ + { orgname: deleteNodeCollectionRecordOne }, + { orgname: deleteNodeCollectionRecordTwo }, + { orgname: deleteNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置删除数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'destroy', exact: true }).click(); + const deleteRecordNodeName = 'Delete record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Delete record-Delete record', { exact: true }).getByRole('textbox').fill(deleteRecordNodeName); + const deleteRecordNode = new DeleteRecordNode(page, deleteRecordNodeName); + await deleteRecordNode.nodeConfigure.click(); + await deleteRecordNode.collectionDropDown.click(); + await page.getByText(deleteNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: deleteNodeFieldDisplayName.toString() }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect(page.getByLabel('block-item-Filter-workflows-Filter').getByLabel('variable-tag')).toHaveText( + `Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`, + ); + await deleteRecordNode.submitButton.click(); + + // 查看删除前数据 + let deleteNodeCollectionData = await apiGetList(deleteNodeCollectionName); + let deleteNodeCollectionDataObj = JSON.parse(JSON.stringify(deleteNodeCollectionData)); + expect(deleteNodeCollectionDataObj.meta.count).toBe(3); + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = deleteNodeCollectionRecordOne; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据删除成功 + deleteNodeCollectionData = await apiGetList(deleteNodeCollectionName); + deleteNodeCollectionDataObj = JSON.parse(JSON.stringify(deleteNodeCollectionData)); + expect(deleteNodeCollectionDataObj.meta.count).toBe(2); + + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/queryRecordNode/QueryRecord.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/queryRecordNode/QueryRecord.test.ts new file mode 100644 index 000000000..f7c2aa531 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/queryRecordNode/QueryRecord.test.ts @@ -0,0 +1,928 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + QueryRecordNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetWorkflow, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, no filter no sort query common table 1 record', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + expect(queryRecordNodeJobResult.orgname).toBe(triggerNodeCollectionRecordOne); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filtering and no sorting, query common table multiple records, set page 1 20 per page', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + expect(queryRecordNodeJobResult.length).toBe(triggerNodeCollectionRecordNumber + 1); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filter ID ascending, query common table with multiple records, set page X to Y per page', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + // 设置排序条件 + await queryRecordNode.addSortFieldsButton.click(); + await page.getByTestId('select-single').click(); + await page.getByRole('option', { name: 'ID', exact: true }).click(); + await page.getByLabel('block-item-Radio.Group-workflows').getByText('ASC').click(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + const pageNumber = faker.number.int({ min: 1, max: 5 }); + const pageSize = faker.number.int({ min: 1, max: 4 }); + await queryRecordNode.pageNumberEditBox.fill(pageNumber.toString()); + await queryRecordNode.pageSizeEditBox.fill(pageSize.toString()); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + let nodeResultRecords = 0; + const recordCount = triggerNodeCollectionRecordNumber + 1; + if (pageNumber > Math.ceil(recordCount / pageSize)) { + nodeResultRecords = 0; + expect(queryRecordNodeJobResult).toEqual([]); + } else if (pageNumber * pageSize > recordCount) { + nodeResultRecords = recordCount - (pageNumber - 1) * pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + // 获取数组中对象下ID的值 + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于总记录数 + expect(Math.max(...nodeResultRecordsIDArray)).toBe(recordCount); + } else { + nodeResultRecords = pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于pageNumber * pageSize + expect(Math.max(...nodeResultRecordsIDArray)).toBe(pageNumber * pageSize); + } + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filter ID descending, query common table with multiple records, set page X to Y per page', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + // 设置排序条件 + await queryRecordNode.addSortFieldsButton.click(); + await page.getByTestId('select-single').click(); + await page.getByRole('option', { name: 'ID', exact: true }).click(); + await page.getByLabel('block-item-Radio.Group-workflows').getByText('DESC').click(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + const pageNumber = faker.number.int({ min: 1, max: 5 }); + const pageSize = faker.number.int({ min: 1, max: 4 }); + await queryRecordNode.pageNumberEditBox.fill(pageNumber.toString()); + await queryRecordNode.pageSizeEditBox.fill(pageSize.toString()); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + let nodeResultRecords = 0; + const recordCount = triggerNodeCollectionRecordNumber + 1; + if (pageNumber > Math.ceil(recordCount / pageSize)) { + nodeResultRecords = 0; + expect(queryRecordNodeJobResult).toEqual([]); + } else if (pageNumber * pageSize > recordCount) { + nodeResultRecords = recordCount - (pageNumber - 1) * pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + // 获取数组中对象下ID的值 + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe(1); + // 断言数组的最大值等于总记录数 + expect(Math.max(...nodeResultRecordsIDArray)).toBe(nodeResultRecords); + } else { + nodeResultRecords = pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe(recordCount - pageNumber * pageSize + 1); + // 断言数组的最大值等于pageNumber * pageSize + expect(Math.max(...nodeResultRecordsIDArray)).toBe(recordCount - (pageNumber - 1) * pageSize); + } + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filtering and no sorting, query multiple records of a common table, set page X to Y per page', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + const pageNumber = faker.number.int({ min: 1, max: 5 }); + const pageSize = faker.number.int({ min: 1, max: 4 }); + await queryRecordNode.pageNumberEditBox.fill(pageNumber.toString()); + await queryRecordNode.pageSizeEditBox.fill(pageSize.toString()); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + let nodeResultRecords = 0; + const recordCount = triggerNodeCollectionRecordNumber + 1; + if (pageNumber > Math.ceil(recordCount / pageSize)) { + nodeResultRecords = 0; + expect(queryRecordNodeJobResult).toEqual([]); + } else if (pageNumber * pageSize > recordCount) { + nodeResultRecords = recordCount - (pageNumber - 1) * pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + // 获取数组中对象下ID的值 + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于总记录数 + expect(Math.max(...nodeResultRecordsIDArray)).toBe(recordCount); + } else { + nodeResultRecords = pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于pageNumber * pageSize + expect(Math.max(...nodeResultRecordsIDArray)).toBe(pageNumber * pageSize); + } + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filtering and no sorting, query the ordinary table 1 record, set the X page Y per page, query the results of the empty, exit the process', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + // await queryRecordNode.allowMultipleDataBoxesForResults.check(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + const pageNumber = faker.number.int({ min: 5, max: 5 }); + const pageSize = faker.number.int({ min: 5, max: 5 }); + await queryRecordNode.pageNumberEditBox.fill(pageNumber.toString()); + await queryRecordNode.pageSizeEditBox.fill(pageSize.toString()); + await queryRecordNode.exitProcessOptionsBoxWithEmptyResult.check(); + await expect(queryRecordNode.exitProcessOptionsBoxWithEmptyResult).toBeChecked(); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + let nodeResultRecords = 0; + const recordCount = triggerNodeCollectionRecordNumber + 1; + if (pageNumber > Math.ceil(recordCount / pageSize)) { + nodeResultRecords = 0; + // expect(queryRecordNodeJobResult).toEqual([]); + expect(queryRecordNodeJobResult).toBeNull(); + } else if (pageNumber * pageSize > recordCount) { + nodeResultRecords = recordCount - (pageNumber - 1) * pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + // 获取数组中对象下ID的值 + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于总记录数 + expect(Math.max(...nodeResultRecordsIDArray)).toBe(recordCount); + } else { + nodeResultRecords = pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于pageNumber * pageSize + expect(Math.max(...nodeResultRecordsIDArray)).toBe(pageNumber * pageSize); + } + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, no filtering and no sorting, query the common table multiple records, set the X page Y per page, query results are empty, exit the process', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = faker.string.alphanumeric(5); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + const triggerNodeCollectionRecordNumber = faker.number.int({ min: 0, max: 19 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, triggerNodeCollectionRecordNumber); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByRole('option', { name: triggerNodeCollectionDisplayName }).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + await expect(queryRecordNode.pageNumberEditBox).toHaveValue('1'); + await expect(queryRecordNode.pageSizeEditBox).toHaveValue('20'); + const pageNumber = faker.number.int({ min: 5, max: 5 }); + const pageSize = faker.number.int({ min: 5, max: 5 }); + await queryRecordNode.pageNumberEditBox.fill(pageNumber.toString()); + await queryRecordNode.pageSizeEditBox.fill(pageSize.toString()); + await queryRecordNode.exitProcessOptionsBoxWithEmptyResult.check(); + await expect(queryRecordNode.exitProcessOptionsBoxWithEmptyResult).toBeChecked(); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await mockRecords(triggerNodeCollectionName, [{ orgname: triggerNodeCollectionRecordOne }]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + let nodeResultRecords = 0; + const recordCount = triggerNodeCollectionRecordNumber + 1; + if (pageNumber > Math.ceil(recordCount / pageSize)) { + nodeResultRecords = 0; + expect(queryRecordNodeJobResult).toEqual([]); + } else if (pageNumber * pageSize > recordCount) { + nodeResultRecords = recordCount - (pageNumber - 1) * pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + // 获取数组中对象下ID的值 + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于总记录数 + expect(Math.max(...nodeResultRecordsIDArray)).toBe(recordCount); + } else { + nodeResultRecords = pageSize; + expect(queryRecordNodeJobResult.length).toBe(nodeResultRecords); + const nodeResultRecordsIDArray = queryRecordNodeJobResult.map((obj) => obj.id); + // 断言数组的最小值等于(页码-1)*每页记录数+1 + expect(Math.min(...nodeResultRecordsIDArray)).toBe((pageNumber - 1) * pageSize + 1); + // 断言数组的最大值等于pageNumber * pageSize + expect(Math.max(...nodeResultRecordsIDArray)).toBe(pageNumber * pageSize); + } + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter to meet all conditions (status_singleselect=1 and staffnum>20) unsorted, query normal table 1 record', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const queryNodeAppendText = 'b' + faker.string.alphanumeric(4); + // 创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建查询节点数据表 + const queryNodeCollectionDisplayName = `自动>组织[普通表]${queryNodeAppendText}`; + const queryNodeCollectionName = `tt_amt_org${queryNodeAppendText}`; + const queryNodeFieldName = 'orgname'; + const queryNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), queryNodeAppendText) + .collections, + ); + const queryNodeCollectioRecordOne = [{ status_singleselect: '1', staffnum: 10 }]; + const queryNodeCollectioRecordTwo = [{ status_singleselect: '1', staffnum: 20 }]; + const queryNodeCollectioRecordThree = [{ status_singleselect: '1', staffnum: 30 }]; + const queryNodeCollectioRecordFour = [{ status_singleselect: '1', staffnum: 40 }]; + const queryNodeCollectioRecordFive = [{ status_singleselect: '2', staffnum: 10 }]; + const queryNodeCollectioRecordSix = [{ status_singleselect: '2', staffnum: 20 }]; + const queryNodeCollectioRecordSeven = [{ status_singleselect: '2', staffnum: 30 }]; + const queryNodeCollectioRecordEight = [{ status_singleselect: '2', staffnum: 40 }]; + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordOne); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordTwo); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordThree); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordFour); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordFive); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordSix); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordSeven); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordEight); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByText(queryNodeCollectionDisplayName).click(); + // await queryRecordNode.allowMultipleDataBoxesForResults.check(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司状态(下拉单选)' }).click(); + await page.getByTestId('select-single').click(); + await page.getByRole('option', { name: '存续' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: '员工人数(整数)' }).click(); + await page.getByRole('button', { name: '=' }).click(); + await page.getByRole('option', { name: '>' }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('spinbutton').fill('20'); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + const nodeResultRecordID = queryRecordNodeJobResult.id; + const expectRecordIDArray = [3, 4]; + // 断言nodeResultRecordID的值在expectRecordIDArray数组中 + expect(expectRecordIDArray).toContain(nodeResultRecordID); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter to satisfy any condition (status_singleselect=1 or staffnum>20) unsorted, query common table for multiple rows, set page X to Y per page', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const queryNodeAppendText = 'b' + faker.string.alphanumeric(4); + // 创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + // 创建查询节点数据表 + const queryNodeCollectionDisplayName = `自动>组织[普通表]${queryNodeAppendText}`; + const queryNodeCollectionName = `tt_amt_org${queryNodeAppendText}`; + const queryNodeFieldName = 'orgname'; + const queryNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), queryNodeAppendText) + .collections, + ); + const queryNodeCollectioRecordOne = [{ status_singleselect: '1', staffnum: 10 }]; + const queryNodeCollectioRecordTwo = [{ status_singleselect: '1', staffnum: 20 }]; + const queryNodeCollectioRecordThree = [{ status_singleselect: '1', staffnum: 30 }]; + const queryNodeCollectioRecordFour = [{ status_singleselect: '1', staffnum: 40 }]; + const queryNodeCollectioRecordFive = [{ status_singleselect: '2', staffnum: 10 }]; + const queryNodeCollectioRecordSix = [{ status_singleselect: '2', staffnum: 20 }]; + const queryNodeCollectioRecordSeven = [{ status_singleselect: '2', staffnum: 30 }]; + const queryNodeCollectioRecordEight = [{ status_singleselect: '2', staffnum: 40 }]; + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordOne); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordTwo); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordThree); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordFour); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordFive); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordSix); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordSeven); + await mockRecords(queryNodeCollectionName, queryNodeCollectioRecordEight); + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置查询节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'query', exact: true }).click(); + const queryRecordNodeName = 'Query record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Query record-Query record', { exact: true }).getByRole('textbox').fill(queryRecordNodeName); + const queryRecordNode = new QueryRecordNode(page, queryRecordNodeName); + const queryRecordNodeId = await queryRecordNode.node.locator('.workflow-node-id').innerText(); + await queryRecordNode.nodeConfigure.click(); + await queryRecordNode.collectionDropDown.click(); + await page.getByText(queryNodeCollectionDisplayName).click(); + await queryRecordNode.allowMultipleDataBoxesForResults.check(); + // 设置过滤条件 + await page.getByTestId('filter-select-all-or-any').click(); + await page.getByRole('option', { name: 'Any' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: '公司状态(下拉单选)' }).click(); + await page.getByTestId('select-single').click(); + await page.getByRole('option', { name: '存续' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('button', { name: 'Select field' }).click(); + await page.getByRole('menuitemcheckbox', { name: '员工人数(整数)' }).click(); + await page.getByRole('button', { name: '=' }).click(); + await page.getByRole('option', { name: '>' }).click(); + await page.getByLabel('block-item-Filter-workflows-Filter').getByRole('spinbutton').fill('20'); + await queryRecordNode.submitButton.click(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据查询成功 + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const queryRecordNodeJob = jobs.find((job) => job.nodeId.toString() === queryRecordNodeId); + const queryRecordNodeJobResult = queryRecordNodeJob.result; + expect(queryRecordNodeJobResult.length).toBe(6); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateInABatch.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateInABatch.test.ts new file mode 100644 index 000000000..e683c4366 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateInABatch.test.ts @@ -0,0 +1,2177 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + UpdateRecordNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetList, + apiGetWorkflow, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, filter single line text field not empty, common table single line text field data, set single line text field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'orgname'; + const updateNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { orgname: updateNodeCollectionRecordOne }, + { orgname: updateNodeCollectionRecordTwo }, + { orgname: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('block-item-CollectionFieldset').getByRole('textbox').fill(updateRecordNodefieldData); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.orgname.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter single line text field not empty, common table single line text field data, set trigger node single line text field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'orgname'; + const updateNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { orgname: updateNodeCollectionRecordOne }, + { orgname: updateNodeCollectionRecordTwo }, + { orgname: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.orgname.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter multi-line text field not empty, common table multi-line text field data, set multi-line text field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'address'; + const triggerNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'address'; + const updateNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { address: updateNodeCollectionRecordOne }, + { address: updateNodeCollectionRecordTwo }, + { address: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('block-item-CollectionFieldset').getByRole('textbox').fill(updateRecordNodefieldData); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:录入数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { address: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.address.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter multiline text field not empty, common table multiline text field data, set trigger node multiline text field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'address'; + const triggerNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'address'; + const updateNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { address: updateNodeCollectionRecordOne }, + { address: updateNodeCollectionRecordTwo }, + { address: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { address: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.address.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter integer field not null, common table integer field data, set integer field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'staffnum'; + const updateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.int(); + const updateNodeCollectionRecordTwo = faker.number.int(); + const updateNodeCollectionRecordThree = faker.number.int(); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { staffnum: updateNodeCollectionRecordOne }, + { staffnum: updateNodeCollectionRecordTwo }, + { staffnum: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = faker.number.int(); + await page + .getByLabel('block-item-CollectionFieldset') + .getByRole('spinbutton') + .fill(updateRecordNodefieldData.toString()); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter integer field not empty, common table integer field data, set trigger node integer field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'staffnum'; + const updateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.int(); + const updateNodeCollectionRecordTwo = faker.number.int(); + const updateNodeCollectionRecordThree = faker.number.int(); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { staffnum: updateNodeCollectionRecordOne }, + { staffnum: updateNodeCollectionRecordTwo }, + { staffnum: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + const updateRecordNodefieldData = faker.number.int(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter numeric field not null, common table numeric field data, set numeric field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'regcapital'; + const triggerNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'regcapital'; + const updateNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + // 定义随机4位数小数的数值 + // const updateNodeCollectionRecordOne = faker.datatype.float({ min: 0, max: 10000, precision: 4 }); + const updateNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordTwo = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordThree = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { regcapital: updateNodeCollectionRecordOne }, + { regcapital: updateNodeCollectionRecordTwo }, + { regcapital: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + await page + .getByLabel('block-item-CollectionFieldset') + .getByRole('spinbutton') + .fill(updateRecordNodefieldData.toString()); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { regcapital: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter numeric field not empty, common table numeric field data, set trigger node numeric field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'regcapital'; + const triggerNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'regcapital'; + const updateNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordTwo = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordThree = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { regcapital: updateNodeCollectionRecordOne }, + { regcapital: updateNodeCollectionRecordTwo }, + { regcapital: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { regcapital: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio field not null, common table dropdown radio field data, set dropdown radio field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'status_singleselect'; + const triggerNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'status_singleselect'; + const updateNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = '1'; + const updateNodeCollectionRecordTwo = '2'; + const updateNodeCollectionRecordThree = '3'; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { status_singleselect: updateNodeCollectionRecordOne }, + { status_singleselect: updateNodeCollectionRecordTwo }, + { status_singleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = '4'; + await page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByTestId('select-single').click(); + await page.getByRole('option', { name: '注销' }).click(); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = '4'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { status_singleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.status_singleselect === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio field not empty, common table dropdown radio field data, set trigger node dropdown radio field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'status_singleselect'; + const triggerNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'status_singleselect'; + const updateNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = '1'; + const updateNodeCollectionRecordTwo = '2'; + const updateNodeCollectionRecordThree = '3'; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { status_singleselect: updateNodeCollectionRecordOne }, + { status_singleselect: updateNodeCollectionRecordTwo }, + { status_singleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = '4'; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { status_singleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.status_singleselect === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio fields not null, common table dropdown radio fields data, set dropdown radio fields constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'range_multipleselect'; + const triggerNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'range_multipleselect'; + const updateNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = ['F3134', 'I3006']; + const updateNodeCollectionRecordTwo = ['F3134', 'I3007']; + const updateNodeCollectionRecordThree = ['I3006', 'I3007']; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { range_multipleselect: updateNodeCollectionRecordOne }, + { range_multipleselect: updateNodeCollectionRecordTwo }, + { range_multipleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByTestId('select-multiple').click(); + await page.getByRole('option', { name: '数据处理服务', exact: true }).click(); + await page.getByRole('option', { name: '计算机系统服务', exact: true }).click(); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = ['I3032', 'I3034']; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = ['I3032', 'I3034']; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { range_multipleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio fields not empty, common table dropdown radio fields data, set trigger node dropdown radio fields variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'range_multipleselect'; + const triggerNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'range_multipleselect'; + const updateNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = ['F3134', 'I3006']; + const updateNodeCollectionRecordTwo = ['F3134', 'I3007']; + const updateNodeCollectionRecordThree = ['I3006', 'I3007']; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { range_multipleselect: updateNodeCollectionRecordOne }, + { range_multipleselect: updateNodeCollectionRecordTwo }, + { range_multipleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = ['I3032', 'I3034']; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = ['I3032', 'I3034']; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { range_multipleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter date field not null, common table date field data, set date field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'establishdate'; + const triggerNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'establishdate'; + const updateNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + // 定义随机4位数小数的数值 + // const updateNodeCollectionRecordOne = faker.datatype.float({ min: 0, max: 10000, precision: 4 }); + const updateNodeCollectionRecordOne = dayjs().add(-3, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordTwo = dayjs().add(-2, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordThree = dayjs().add(-1, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { establishdate: updateNodeCollectionRecordOne }, + { establishdate: updateNodeCollectionRecordTwo }, + { establishdate: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = dayjs().format('YYYY-MM-DD'); + await page.getByLabel('block-item-CollectionFieldset').getByPlaceholder('Select date').click(); + await page + .getByLabel('block-item-CollectionFieldset') + .getByPlaceholder('Select date') + .fill(updateRecordNodefieldData); + await page.getByTitle(updateRecordNodefieldData.toString()).locator('div').click(); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = dayjs().format('YYYY-MM-DD'); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { establishdate: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter date field not empty, common table date field data, set trigger node date field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'establishdate'; + const triggerNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'establishdate'; + const updateNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = dayjs().add(-3, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordTwo = dayjs().add(-2, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordThree = dayjs().add(-1, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { establishdate: updateNodeCollectionRecordOne }, + { establishdate: updateNodeCollectionRecordTwo }, + { establishdate: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = dayjs().format('YYYY-MM-DD'); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = dayjs().format('YYYY-MM-DD'); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { establishdate: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateOneByOne.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateOneByOne.test.ts new file mode 100644 index 000000000..d0a6a3ef9 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/__e2e__/updateRecordNode/updateOneByOne.test.ts @@ -0,0 +1,2191 @@ +import { faker } from '@faker-js/faker'; +import { + CollectionTriggerNode, + UpdateRecordNode, + apiCreateWorkflow, + apiDeleteWorkflow, + apiGetList, + apiGetWorkflow, + apiGetWorkflowNodeExecutions, + apiUpdateWorkflowTrigger, + appendJsonCollectionName, + generalWithNoRelationalFields, +} from '@nocobase/plugin-workflow-test/e2e'; +import { expect, test } from '@nocobase/test/e2e'; +import { dayjs } from '@nocobase/utils'; + +test('Collection event add data trigger, filter single line text field not empty, common table single line text field data, set single line text field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'orgname'; + const updateNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { orgname: updateNodeCollectionRecordOne }, + { orgname: updateNodeCollectionRecordTwo }, + { orgname: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('block-item-CollectionFieldset').getByRole('textbox').fill(updateRecordNodefieldData); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.orgname.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter single line text field not empty, common table single line text field data, set trigger node single line text field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'orgname'; + const triggerNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'orgname'; + const updateNodeFieldDisplayName = '公司名称(单行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { orgname: updateNodeCollectionRecordOne }, + { orgname: updateNodeCollectionRecordTwo }, + { orgname: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.orgname.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.orgname.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter multi-line text field not empty, common table multi-line text field data, set multi-line text field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'address'; + const triggerNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'address'; + const updateNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { address: updateNodeCollectionRecordOne }, + { address: updateNodeCollectionRecordTwo }, + { address: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('block-item-CollectionFieldset').getByRole('textbox').fill(updateRecordNodefieldData); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:录入数据触发工作流 + const triggerNodeCollectionRecordOne = triggerNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { address: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.address.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter multiline text field not empty, common table multiline text field data, set trigger node multiline text field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'address'; + const triggerNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'address'; + const updateNodeFieldDisplayName = '公司地址(多行文本)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = + updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordTwo = + updateNodeFieldDisplayName + dayjs().add(5, 'second').format('YYYYMMDDHHmmss.SSS').toString() + faker.lorem.word(4); + const updateNodeCollectionRecordThree = + updateNodeFieldDisplayName + + dayjs().add(10, 'second').format('YYYYMMDDHHmmss.SSS').toString() + + faker.lorem.word(4); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { address: updateNodeCollectionRecordOne }, + { address: updateNodeCollectionRecordTwo }, + { address: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = updateNodeFieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { address: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.address.toString() === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.address.toString() === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter integer field not null, common table integer field data, set integer field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'staffnum'; + const updateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.int(); + const updateNodeCollectionRecordTwo = faker.number.int(); + const updateNodeCollectionRecordThree = faker.number.int(); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { staffnum: updateNodeCollectionRecordOne }, + { staffnum: updateNodeCollectionRecordTwo }, + { staffnum: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = faker.number.int(); + await page + .getByLabel('block-item-CollectionFieldset') + .getByRole('spinbutton') + .fill(updateRecordNodefieldData.toString()); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.int(); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { orgname: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter integer field not empty, common table integer field data, set trigger node integer field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'staffnum'; + const triggerNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'staffnum'; + const updateNodeFieldDisplayName = '员工人数(整数)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.int(); + const updateNodeCollectionRecordTwo = faker.number.int(); + const updateNodeCollectionRecordThree = faker.number.int(); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { staffnum: updateNodeCollectionRecordOne }, + { staffnum: updateNodeCollectionRecordTwo }, + { staffnum: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + const updateRecordNodefieldData = faker.number.int(); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { staffnum: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.staffnum === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.staffnum === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter numeric field not null, common table numeric field data, set numeric field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'regcapital'; + const triggerNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'regcapital'; + const updateNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + // 定义随机4位数小数的数值 + // const updateNodeCollectionRecordOne = faker.datatype.float({ min: 0, max: 10000, precision: 4 }); + const updateNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordTwo = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordThree = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { regcapital: updateNodeCollectionRecordOne }, + { regcapital: updateNodeCollectionRecordTwo }, + { regcapital: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + await page + .getByLabel('block-item-CollectionFieldset') + .getByRole('spinbutton') + .fill(updateRecordNodefieldData.toString()); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { regcapital: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter numeric field not empty, common table numeric field data, set trigger node numeric field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'regcapital'; + const triggerNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'regcapital'; + const updateNodeFieldDisplayName = '注册资本(数字)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordTwo = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecordThree = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { regcapital: updateNodeCollectionRecordOne }, + { regcapital: updateNodeCollectionRecordTwo }, + { regcapital: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = faker.number.float({ min: 0, max: 999999999, precision: 0.0001 }); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { regcapital: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.regcapital === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.regcapital === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio field not null, common table dropdown radio field data, set dropdown radio field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'status_singleselect'; + const triggerNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'status_singleselect'; + const updateNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = '1'; + const updateNodeCollectionRecordTwo = '2'; + const updateNodeCollectionRecordThree = '3'; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { status_singleselect: updateNodeCollectionRecordOne }, + { status_singleselect: updateNodeCollectionRecordTwo }, + { status_singleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = '4'; + await page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByTestId('select-single').click(); + await page.getByRole('option', { name: '注销' }).click(); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = '4'; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { status_singleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.status_singleselect === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio field not empty, common table dropdown radio field data, set trigger node dropdown radio field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'status_singleselect'; + const triggerNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'status_singleselect'; + const updateNodeFieldDisplayName = '公司状态(下拉单选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = '1'; + const updateNodeCollectionRecordTwo = '2'; + const updateNodeCollectionRecordThree = '3'; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { status_singleselect: updateNodeCollectionRecordOne }, + { status_singleselect: updateNodeCollectionRecordTwo }, + { status_singleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = '4'; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = updateRecordNodefieldData; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { status_singleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.status_singleselect === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find((arr) => arr.status_singleselect === updateRecordNodefieldData); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio fields not null, common table dropdown radio fields data, set dropdown radio fields constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'range_multipleselect'; + const triggerNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'range_multipleselect'; + const updateNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = ['F3134', 'I3006']; + const updateNodeCollectionRecordTwo = ['F3134', 'I3007']; + const updateNodeCollectionRecordThree = ['I3006', 'I3007']; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { range_multipleselect: updateNodeCollectionRecordOne }, + { range_multipleselect: updateNodeCollectionRecordTwo }, + { range_multipleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByTestId('select-multiple').click(); + await page.getByRole('option', { name: '数据处理服务', exact: true }).click(); + await page.getByRole('option', { name: '计算机系统服务', exact: true }).click(); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = ['I3032', 'I3034']; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = ['I3032', 'I3034']; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { range_multipleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter dropdown radio fields not empty, common table dropdown radio fields data, set trigger node dropdown radio fields variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'range_multipleselect'; + const triggerNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'range_multipleselect'; + const updateNodeFieldDisplayName = '经营范围(下拉多选)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = ['F3134', 'I3006']; + const updateNodeCollectionRecordTwo = ['F3134', 'I3007']; + const updateNodeCollectionRecordThree = ['I3006', 'I3007']; + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { range_multipleselect: updateNodeCollectionRecordOne }, + { range_multipleselect: updateNodeCollectionRecordTwo }, + { range_multipleselect: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = ['I3032', 'I3034']; + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = ['I3032', 'I3034']; + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { range_multipleselect: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordOne.toString(), + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordTwo.toString(), + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateNodeCollectionRecordThree.toString(), + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => arr.range_multipleselect.toString() === updateRecordNodefieldData.toString(), + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter date field not null, common table date field data, set date field constant data', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'establishdate'; + const triggerNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'establishdate'; + const updateNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + // 定义随机4位数小数的数值 + // const updateNodeCollectionRecordOne = faker.datatype.float({ min: 0, max: 10000, precision: 4 }); + const updateNodeCollectionRecordOne = dayjs().add(-3, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordTwo = dayjs().add(-2, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordThree = dayjs().add(-1, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { establishdate: updateNodeCollectionRecordOne }, + { establishdate: updateNodeCollectionRecordTwo }, + { establishdate: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + const updateRecordNodefieldData = dayjs().format('YYYY-MM-DD'); + await page.getByLabel('block-item-CollectionFieldset').getByPlaceholder('Select date').click(); + await page + .getByLabel('block-item-CollectionFieldset') + .getByPlaceholder('Select date') + .fill(updateRecordNodefieldData); + await page.getByTitle(updateRecordNodefieldData.toString()).locator('div').click(); + await updateRecordNode.submitButton.click(); + + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = dayjs().format('YYYY-MM-DD'); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { establishdate: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); + +test('Collection event add data trigger, filter date field not empty, common table date field data, set trigger node date field variable', async ({ + page, + mockCollections, + mockRecords, +}) => { + //数据表后缀标识 + const triggerNodeAppendText = 'a' + faker.string.alphanumeric(4); + const updateNodeAppendText = 'b' + faker.string.alphanumeric(4); + //创建触发器节点数据表 + const triggerNodeCollectionDisplayName = `自动>组织[普通表]${triggerNodeAppendText}`; + const triggerNodeCollectionName = `tt_amt_org${triggerNodeAppendText}`; + const triggerNodeFieldName = 'establishdate'; + const triggerNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), triggerNodeAppendText) + .collections, + ); + + // 创建更新节点数据表 + const updateNodeCollectionDisplayName = `自动>组织[普通表]${updateNodeAppendText}`; + const updateNodeCollectionName = `tt_amt_org${updateNodeAppendText}`; + const updateNodeFieldName = 'establishdate'; + const updateNodeFieldDisplayName = '成立日期(日期)'; + await mockCollections( + appendJsonCollectionName(JSON.parse(JSON.stringify(generalWithNoRelationalFields)), updateNodeAppendText) + .collections, + ); + const updateNodeCollectionRecordOne = dayjs().add(-3, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordTwo = dayjs().add(-2, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecordThree = dayjs().add(-1, 'day').format('YYYY-MM-DD'); + const updateNodeCollectionRecords = await mockRecords(updateNodeCollectionName, [ + { establishdate: updateNodeCollectionRecordOne }, + { establishdate: updateNodeCollectionRecordTwo }, + { establishdate: updateNodeCollectionRecordThree }, + ]); + + //添加工作流 + const workFlowName = faker.string.alphanumeric(5) + triggerNodeAppendText; + const workflowData = { + current: true, + options: { deleteExecutionOnStatus: [] }, + title: workFlowName, + type: 'collection', + enabled: true, + }; + const workflow = await apiCreateWorkflow(workflowData); + const workflowObj = JSON.parse(JSON.stringify(workflow)); + const workflowId = workflowObj.id; + //配置工作流触发器 + const triggerNodeData = { + config: { appends: [], collection: triggerNodeCollectionName, changed: [], condition: { $and: [] }, mode: 1 }, + }; + const triggerNode = await apiUpdateWorkflowTrigger(workflowId, triggerNodeData); + const triggerNodeObj = JSON.parse(JSON.stringify(triggerNode)); + + //配置更新数据节点 + await page.goto(`admin/workflow/workflows/${workflowId}`); + await page.waitForLoadState('networkidle'); + const collectionTriggerNode = new CollectionTriggerNode(page, workFlowName, triggerNodeCollectionName); + await collectionTriggerNode.addNodeButton.click(); + await page.getByRole('button', { name: 'update', exact: true }).click(); + const updateRecordNodeName = 'Update record' + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); + await page.getByLabel('Update record-Update record', { exact: true }).getByRole('textbox').fill(updateRecordNodeName); + const updateRecordNode = new UpdateRecordNode(page, updateRecordNodeName); + const updateRecordNodeId = await updateRecordNode.node.locator('.workflow-node-id').innerText(); + await updateRecordNode.nodeConfigure.click(); + await updateRecordNode.collectionDropDown.click(); + await page.getByText(updateNodeCollectionDisplayName).click(); + await updateRecordNode.articleByArticleUpdateModeRadio.click(); + // 设置过滤条件 + await page.getByText('Add condition', { exact: true }).click(); + await page + .getByLabel('block-item-Filter-workflows-Only update records matching conditions') + .getByRole('button', { name: 'Select field' }) + .click(); + await page.getByRole('menuitemcheckbox', { name: updateNodeFieldDisplayName.toString() }).click(); + await page.getByTestId('select-filter-operator').click(); + await page.getByRole('option', { name: 'is not empty' }).click(); + + // 设置字段 + await updateRecordNode.addFieldsButton.click(); + await page.getByRole('menuitem', { name: updateNodeFieldDisplayName }).click(); + await page.getByLabel('variable-button').click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger variables' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Trigger data' }).click(); + await page.getByRole('menuitemcheckbox', { name: triggerNodeFieldDisplayName }).click(); + await expect( + page.getByLabel('block-item-CollectionFieldset-workflows-Fields values').getByLabel('variable-tag'), + ).toHaveText(`Trigger variables / Trigger data / ${triggerNodeFieldDisplayName}`); + await updateRecordNode.submitButton.click(); + + const updateRecordNodefieldData = dayjs().format('YYYY-MM-DD'); + // 查看更新前数据 + let updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + let updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + let updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + let recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeTruthy(); + let recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeTruthy(); + let recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeTruthy(); + let afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeFalsy(); + + // 2、测试步骤:添加数据触发工作流 + const triggerNodeCollectionRecordOne = dayjs().format('YYYY-MM-DD'); + const triggerNodeCollectionRecords = await mockRecords(triggerNodeCollectionName, [ + { establishdate: triggerNodeCollectionRecordOne }, + ]); + await page.waitForTimeout(1000); + + // 3、预期结果:工作流成功触发,数据更新成功 + updateNodeCollectionData = await apiGetList(updateNodeCollectionName); + updateNodeCollectionDataObj = JSON.parse(JSON.stringify(updateNodeCollectionData)); + updateNodeCollectionDataArr = updateNodeCollectionDataObj.data; + recordOnebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordOne, + ); + expect(recordOnebeforeUpdateExpect).toBeFalsy(); + recordTwobeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordTwo, + ); + expect(recordTwobeforeUpdateExpect).toBeFalsy(); + recordThreebeforeUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateNodeCollectionRecordThree, + ); + expect(recordThreebeforeUpdateExpect).toBeFalsy(); + afterUpdateExpect = updateNodeCollectionDataArr.find( + (arr) => dayjs(arr.establishdate).format('YYYY-MM-DD') === updateRecordNodefieldData, + ); + expect(afterUpdateExpect).toBeTruthy(); + + const getWorkflow = await apiGetWorkflow(workflowId); + const getWorkflowObj = JSON.parse(JSON.stringify(getWorkflow)); + const getWorkflowExecuted = getWorkflowObj.executed; + expect(getWorkflowExecuted).toBe(1); + const getWorkflowNodeExecutions = await apiGetWorkflowNodeExecutions(workflowId); + const getWorkflowNodeExecutionsObj = JSON.parse(JSON.stringify(getWorkflowNodeExecutions)); + getWorkflowNodeExecutionsObj.sort(function (a: { id: number }, b: { id: number }) { + return b.id - a.id; + }); + const jobs = getWorkflowNodeExecutionsObj[0].jobs; + const updateRecordNodeJob = jobs.find((job) => job.nodeId.toString() === updateRecordNodeId); + const updateRecordNodeJobResult = updateRecordNodeJob.result; + expect(updateRecordNodeJobResult).toBe(3); + // 4、后置处理:删除工作流 + await apiDeleteWorkflow(workflowId); +}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/createFormEvent.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/createFormEvent.test.ts deleted file mode 100644 index 22f82454f..000000000 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/createFormEvent.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect, test } from '@nocobase/test/e2e'; -import { dayjs } from '@nocobase/utils'; -import { CreateWorkFlow } from './pageobject/workFlow'; - -test.describe('workflow manage', () => { - test('add new from event', async ({ page }) => { - //用例编号 - const caseNum = 'WF02AA'; - //用例标题 - const caseTitle = 'add new from event'; - - // 1、前置条件:已登录 - - // 2、测试步骤:进入“工作流管理”-“新建”按钮,填写表单,点击“确定”按钮 - await page.goto('/admin/settings/workflow'); - await page.waitForLoadState('networkidle'); - await page.getByLabel('action-Action-Add new-workflows').click(); - const createWorkFlow = new CreateWorkFlow(page); - const workFlowName = caseTitle + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); - await createWorkFlow.name.fill(workFlowName); - await createWorkFlow.triggerType.click(); - await page.getByRole('option', { name: 'Form event' }).click(); - await page.getByLabel('action-Action-Submit-workflows').click(); - - // 3、预期结果:新建成功,列表中出现新建的工作流 - await expect(page.getByText(workFlowName)).toBeVisible(); - - // 4、后置处理:删除新建的工作流 - await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); - await page.getByRole('textbox').fill(workFlowName); - await page.getByRole('button', { name: 'Submit' }).click(); - await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect(page.getByText(workFlowName)).toBeHidden(); - }); -}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/editFormEvent.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/editFormEvent.test.ts deleted file mode 100644 index 5422ef2f7..000000000 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/editFormEvent.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { expect, test } from '@nocobase/test/e2e'; -import { dayjs } from '@nocobase/utils'; -import { CreateWorkFlow, EditWorkFlow } from './pageobject/workFlow'; - -test.describe('workflow manage', () => { - test('edit from event name', async ({ page }) => { - //用例编号 - const caseNum = 'WF05AA'; - //用例标题 - const caseTitle = 'edit from event name'; - - // 1、前置条件:1.1、已登录,1.2、存在一个工作流 - await page.goto('/admin/settings/workflow'); - await page.waitForLoadState('networkidle'); - await page.getByLabel('action-Action-Add new-workflows').click(); - const createWorkFlow = new CreateWorkFlow(page); - let workFlowName = caseTitle + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); - await createWorkFlow.name.fill(workFlowName); - await createWorkFlow.triggerType.click(); - await page.getByRole('option', { name: 'Form event' }).click(); - await page.getByLabel('action-Action-Submit-workflows').click(); - - await expect(page.getByText(workFlowName)).toBeAttached(); - - // 2、测试步骤:进入“工作流管理”-“编辑”按钮,编辑名称,点击“确定”按钮 - await page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`).click(); - const editWorkFlow = new EditWorkFlow(page); - workFlowName = caseTitle + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); - await createWorkFlow.name.fill(workFlowName); - await page.getByLabel('action-Action-Submit-workflows').click(); - - // 3、预期结果:编辑成功,列表中出现编辑后的工作流 - await expect(page.getByText(workFlowName)).toBeAttached(); - - // 4、后置处理:删除工作流 - await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); - await page.getByRole('textbox').fill(workFlowName); - await page.getByRole('button', { name: 'Submit' }).click(); - await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect(page.getByText(workFlowName)).toBeHidden(); - }); -}); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/e2eTemplateJson.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/e2eTemplateJson.ts deleted file mode 100644 index 8f82d5a73..000000000 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/e2eTemplateJson.ts +++ /dev/null @@ -1,1751 +0,0 @@ -import { dayjs } from '@nocobase/utils'; -import { lowerCase } from 'lodash'; - -/** - * 为json对象中collections节点的name、title属性添加后缀 - * 为json对象中fields节点的collectionName、through、target性添加后缀 - * @param jsonObj json对象 - * @param appendText 后缀文本 - */ -export function appendJsonCollectionName(jsonObj: any, appendText: string) { - appendCollectionsNode(jsonObj, appendText); - for (const item of jsonObj['collections'][0]['fields']) { - appendFieldsNode(item, appendText); - } - return jsonObj; -} - -function appendCollectionsNode(jsonObj: any, appendText: string) { - jsonObj['collections'][0]['name'] += appendText; - jsonObj['collections'][0]['title'] += appendText; -} -function appendFieldsNode(jsonObj: any, appendText: string) { - for (const key in jsonObj) { - if (key === 'collectionName' || key === 'through' || key === 'target') { - jsonObj[key] += appendText; - } - } -} -/** - * @returns 生成3位随机小写字母 - */ -export function generateRandomLetters() { - let result = ''; - const characters = 'abcdefghijklmnopqrstuvwxyz'; - for (let i = 0; i < 3; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); - } - return result; -} -/** - * builtin_ 为NocoBase内置模板,同NocoBase在页面创建效果一致主要用法3种: - * 1是测试用例仅需一个无业务字段的表即可验证, - * 2是作为多对多的中间表, - * 3是存在先表后关系字段的情况,使用create内置模板表再update表的方式更新数据表来添加全部字段 - * e2e_ 按照不同场景的数据表,可以多个测试用例共用一个或一套数据表 - */ -export const builtin_General = { - collections: [ - { - key: 'hqh4qy21m46', - name: 'tt_amt_org', - title: '自动>组织[普通表]', - inherit: false, - hidden: false, - description: null, - fields: [ - { - key: 'z3fvals50xp', - name: 'id', - type: 'bigInt', - interface: 'id', - description: null, - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: '3cp1zy4ogle', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - description: null, - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: '6l38n4nt7nb', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - description: null, - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: '3xds0p7ersr', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - description: null, - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'slqyx974mzc', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - description: null, - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - ], - category: [], - logging: true, - autoGenId: true, - createdBy: true, - updatedBy: true, - createdAt: true, - updatedAt: true, - sortable: true, - template: 'general', - view: false, - schema: 'public', - }, - ], -}; -export const builtin_Calendar = { - collections: [ - { - key: '7lfgblnyr0w', - name: 'tt_amt_org_cld', - title: '自动>组织[日历表]', - inherit: false, - hidden: false, - description: null, - fields: [ - { - key: 'nq6a40fz0nd', - name: 'cron', - type: 'string', - interface: 'select', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - title: '{{t("Repeats")}}', - 'x-component': 'CronSet', - 'x-component-props': 'allowClear', - enum: [ - { - label: '{{t("Daily")}}', - value: '0 0 0 * * ?', - }, - { - label: '{{t("Weekly")}}', - value: 'every_week', - }, - { - label: '{{t("Monthly")}}', - value: 'every_month', - }, - { - label: '{{t("Yearly")}}', - value: 'every_year', - }, - ], - }, - }, - { - key: 'qtxt638o4nq', - name: 'exclude', - type: 'json', - interface: null, - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - }, - { - key: 'ly88an4fg8f', - name: 'id', - type: 'bigInt', - interface: 'id', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: '8fiqtpovg79', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'hup33jdnp9j', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: '4ky6wvfhduq', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: '1xht59blk26', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - description: null, - collectionName: 'tt_amt_org_cld', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - ], - category: [], - logging: true, - template: 'calendar', - view: false, - createdBy: true, - updatedBy: true, - createdAt: true, - updatedAt: true, - sortable: true, - schema: 'public', - }, - ], -}; -export const builtin_Tree = { - collections: [ - { - key: 'wvgsufr1uxv', - name: 'tt_amt_org_tree', - title: '自动>组织[树表]', - inherit: false, - hidden: false, - description: null, - fields: [ - { - key: '4ihc87rzn00', - name: 'parentId', - type: 'bigInt', - interface: 'integer', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - isForeignKey: true, - uiSchema: { - type: 'number', - title: '{{t("Parent ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - target: 'tt_amt_org_tree', - }, - { - key: 'j58bfihyjdy', - name: 'parent', - type: 'belongsTo', - interface: 'm2o', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - foreignKey: 'parentId', - treeParent: true, - onDelete: 'CASCADE', - uiSchema: { - title: '{{t("Parent")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - multiple: false, - fieldNames: { - label: 'id', - value: 'id', - }, - }, - }, - target: 'tt_amt_org_tree', - targetKey: 'id', - }, - { - key: '6r6b98g0hcz', - name: 'children', - type: 'hasMany', - interface: 'o2m', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - foreignKey: 'parentId', - treeChildren: true, - onDelete: 'CASCADE', - uiSchema: { - title: '{{t("Children")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - multiple: true, - fieldNames: { - label: 'id', - value: 'id', - }, - }, - }, - target: 'tt_amt_org_tree', - targetKey: 'id', - sourceKey: 'id', - }, - { - key: 'hju1ggqps4n', - name: 'id', - type: 'bigInt', - interface: 'id', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: '1y7rpmh8muj', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'ieknujjdkv9', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: 'cv35rzoqna6', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'z21wfpry7uh', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - description: null, - collectionName: 'tt_amt_org_tree', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - ], - category: [], - logging: true, - autoGenId: true, - createdBy: true, - updatedBy: true, - createdAt: true, - updatedAt: true, - sortable: true, - template: 'tree', - view: false, - tree: 'adjacencyList', - schema: 'public', - }, - ], -}; -export const builtin_File = { - collections: [ - { - key: '6hzed4cvpxo', - name: 'tt_amt_file', - title: '自动>档案[文件表]', - inherit: false, - hidden: false, - description: null, - fields: [ - { - key: 'qo0w8dcl45u', - name: 'title', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("Title")}}', - 'x-component': 'Input', - }, - }, - { - key: 'ayzc9b3aee5', - name: 'filename', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("File name", { ns: "file-manager" })}}', - 'x-component': 'Input', - 'x-read-pretty': true, - }, - }, - { - key: 'ftn60zv1rfr', - name: 'extname', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("Extension name", { ns: "file-manager" })}}', - 'x-component': 'Input', - 'x-read-pretty': true, - }, - }, - { - key: '59oj6m0r8be', - name: 'size', - type: 'integer', - interface: 'integer', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'number', - title: '{{t("Size", { ns: "file-manager" })}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - 'x-component-props': { - stringMode: true, - step: '0', - }, - }, - }, - { - key: 'burt1tfbcjx', - name: 'mimetype', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("Mime type", { ns: "file-manager" })}}', - 'x-component': 'Input', - 'x-read-pretty': true, - }, - }, - { - key: '1zfhbqod0i1', - name: 'path', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("Path")}}', - 'x-component': 'Input', - 'x-read-pretty': true, - }, - }, - { - key: 'b0bmsmrfqqw', - name: 'url', - type: 'string', - interface: 'input', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("URL")}}', - 'x-component': 'Input.URL', - 'x-read-pretty': true, - }, - }, - { - key: 'indgsp9sqqq', - name: 'preview', - type: 'string', - interface: 'url', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - field: 'url', - deletable: false, - uiSchema: { - type: 'string', - title: '{{t("Preview")}}', - 'x-component': 'Preview', - 'x-read-pretty': true, - }, - }, - { - key: '2p8d1h46x9l', - name: 'storage', - type: 'belongsTo', - interface: null, - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - comment: '存储引擎', - target: 'storages', - foreignKey: 'storageId', - deletable: false, - targetKey: 'id', - }, - { - key: 'im0yegvb5jt', - name: 'meta', - type: 'jsonb', - interface: null, - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - deletable: false, - defaultValue: {}, - }, - { - key: 'tjr9nr5thoh', - name: 'id', - type: 'bigInt', - interface: 'id', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: 's48vjiqbjcq', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'y3t7jii4m8w', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: 'z5gvejdbfyb', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'qgd8zko7b0z', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - description: null, - collectionName: 'tt_amt_file', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - ], - category: [], - logging: true, - template: 'file', - view: false, - createdBy: true, - updatedBy: true, - storage: 'local', - schema: 'public', - }, - ], -}; -export const builtin_Expression = { - collections: [ - { - key: 'w77iro3197g', - name: 'tt_amt_exp', - title: '自动>表达式[表达式表]', - inherit: false, - hidden: false, - description: null, - fields: [ - { - key: 'slc2i7yz2ck', - name: 'engine', - type: 'string', - interface: 'radioGroup', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - title: '{{t("Calculation engine")}}', - 'x-component': 'Radio.Group', - enum: [ - { - value: 'math.js', - label: 'Math.js', - tooltip: - "{{t('Math.js comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types')}}", - link: 'https://mathjs.org/', - }, - { - value: 'formula.js', - label: 'Formula.js', - tooltip: '{{t("Formula.js supports most Microsoft Excel formula functions.")}}', - link: 'https://formulajs.info/functions/', - }, - ], - default: 'formula.js', - }, - }, - { - key: 'mlvkdcg2yvq', - name: 'sourceCollection', - type: 'string', - interface: 'select', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - title: '{{t("Collection")}}', - 'x-component': 'CollectionSelect', - 'x-component-props': {}, - }, - }, - { - key: 'vdzigakt9ne', - name: 'expression', - type: 'text', - interface: 'expression', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - title: '{{t("Expression")}}', - 'x-component': 'DynamicExpression', - }, - }, - { - key: 'fx72pqt0fjv', - name: 'id', - type: 'bigInt', - interface: 'id', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: 'owiio66cw1u', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: '9fu8kmc69dj', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: 'uie8q16z2jj', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: '6jzyuc1jzro', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - description: null, - collectionName: 'tt_amt_exp', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - ], - category: [], - logging: true, - template: 'expression', - view: false, - createdBy: true, - updatedBy: true, - createdAt: true, - updatedAt: true, - sortable: true, - schema: 'public', - }, - ], -}; -//普通表单表场景,无关系字段情况 -export const e2e_GeneralFormsTable = { - collections: [ - { - key: '86lpobjxamt', - name: 'tt_amt_org', - title: '自动>组织[普通表]', - inherit: false, - hidden: false, - fields: [ - { - key: 'ohxa8xp9amt', - name: 'id', - type: 'bigInt', - interface: 'id', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { - type: 'number', - title: '{{t("ID")}}', - 'x-component': 'InputNumber', - 'x-read-pretty': true, - }, - }, - { - key: 'qjit9pcvamt', - name: 'createdAt', - type: 'date', - interface: 'createdAt', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - field: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'vijs3wcnamt', - name: 'createdBy', - type: 'belongsTo', - interface: 'createdBy', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'createdById', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: 'zsebjtwiamt', - name: 'updatedAt', - type: 'date', - interface: 'updatedAt', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - field: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - key: 'fa4c74phamt', - name: 'updatedBy', - type: 'belongsTo', - interface: 'updatedBy', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - target: 'users', - foreignKey: 'updatedById', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'AssociationField', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - targetKey: 'id', - }, - { - key: 'mv6h7f8lamt', - name: 'orgcode', - type: 'sequence', - interface: 'sequence', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - patterns: [ - { - type: 'string', - options: { - value: '区域编码', - }, - }, - { - type: 'integer', - options: { - digits: 9, - start: 1, - key: 57450, - }, - }, - ], - uiSchema: { - type: 'string', - 'x-component': 'Input', - 'x-component-props': {}, - title: '公司编号(自动编码)', - }, - inputable: false, - }, - { - key: '44u5zoyyamt', - name: 'orgname', - type: 'string', - interface: 'input', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'Input', - title: '公司名称(单行文本)', - }, - }, - { - key: 'tdq82bb0amt', - name: 'address', - type: 'text', - interface: 'textarea', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'Input.TextArea', - title: '公司地址(多行文本)', - }, - }, - { - key: '10xi6i24amt', - name: 'phone', - type: 'string', - interface: 'phone', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'Input', - 'x-component-props': { - type: 'tel', - }, - title: '负责人电话(手机号码)', - }, - }, - { - key: 'n3ykqhe4amt', - name: 'email', - type: 'string', - interface: 'email', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'Input', - 'x-validator': 'email', - title: '电子邮箱(电子邮箱)', - }, - }, - { - key: '5ddcgbpqamt', - name: 'staffnum', - type: 'bigInt', - interface: 'integer', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'number', - 'x-component': 'InputNumber', - 'x-component-props': { - stringMode: true, - step: '1', - }, - 'x-validator': 'integer', - title: '员工人数(整数)', - }, - }, - { - key: 'z59sf4ilamt', - name: 'insurednum', - type: 'bigInt', - interface: 'integer', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'number', - 'x-component': 'InputNumber', - 'x-component-props': { - stringMode: true, - step: '1', - }, - 'x-validator': 'integer', - title: '参保人数(整数)', - }, - }, - { - key: 'ecvb3a8xamt', - name: 'regcapital', - type: 'double', - interface: 'number', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - 'x-component-props': { - step: '0.0001', - stringMode: true, - }, - type: 'number', - 'x-component': 'InputNumber', - title: '注册资本(数字)', - }, - }, - { - key: 'ierg1fbvamt', - name: 'paidcapital', - type: 'double', - interface: 'number', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - 'x-component-props': { - step: '0.0001', - stringMode: true, - }, - type: 'number', - 'x-component': 'InputNumber', - title: '实缴资本(数字)', - }, - }, - { - key: '46kjnywaamt', - name: 'insuranceratio', - type: 'float', - interface: 'percent', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - 'x-component-props': { - step: '0.01', - stringMode: true, - addonAfter: '%', - }, - type: 'string', - 'x-component': 'Percent', - title: '参保占比(百分比)', - }, - }, - { - key: '8j2vkys9amt', - name: 'isenable', - type: 'boolean', - interface: 'checkbox', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'boolean', - 'x-component': 'Checkbox', - title: '是否启用(勾选)', - }, - }, - { - key: '6f480de5amt', - name: 'status_singleselect', - type: 'string', - interface: 'select', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - enum: [ - { - value: '1', - label: '存续', - }, - { - value: '2', - label: '在业', - }, - { - value: '3', - label: '吊销', - }, - { - value: '4', - label: '注销', - }, - { - value: '5', - label: '迁入', - }, - { - value: '6', - label: '迁出', - }, - { - value: '7', - label: '停业', - }, - { - value: '8', - label: '清算', - }, - ], - type: 'string', - 'x-component': 'Select', - title: '公司状态(下拉单选)', - }, - }, - { - key: 'fcx8cw1ramt', - name: 'range_multipleselect', - type: 'array', - interface: 'multipleSelect', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - enum: [ - { - value: 'F3134', - label: '软件销售', - }, - { - value: 'I3006', - label: '软件开发', - }, - { - value: 'I3007', - label: '人工智能基础软件开发', - }, - { - value: 'I3008', - label: '人工智能应用软件开发', - }, - { - value: 'I3010', - label: '软件外包服务', - }, - { - value: 'I3011', - label: '网络与信息安全软件开发', - }, - { - value: 'I3012', - label: '人工智能理论与算法软件开发', - }, - { - value: 'I3014', - label: '数字文化创意软件开发', - }, - { - value: 'I3027', - label: '信息技术咨询服务', - }, - { - value: 'I3032', - label: '数据处理服务', - }, - { - value: 'I3034', - label: '计算机系统服务', - }, - { - value: 'L2032', - label: '信息咨询服务(不含许可类信息咨询服务)', - }, - { - value: 'L2095', - label: '企业管理咨询', - }, - { - value: 'M2070', - label: '技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广', - }, - { - value: 'O3010', - label: '咨询策划服务', - }, - { - value: 'P1029', - label: '业务培训(不含教育培训、职业技能培训等需取得许可的培训)', - }, - ], - type: 'array', - 'x-component': 'Select', - 'x-component-props': { - mode: 'multiple', - }, - title: '经营范围(下拉多选)', - }, - defaultValue: [], - }, - { - key: 'z71al7aaamt', - name: 'status_radio', - type: 'string', - interface: 'radioGroup', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - enum: [ - { - value: '1', - label: '存续', - }, - { - value: '2', - label: '在业', - }, - { - value: '3', - label: '吊销', - }, - { - value: '4', - label: '注销', - }, - { - value: '5', - label: '迁入', - }, - { - value: '6', - label: '迁出', - }, - { - value: '7', - label: '停业', - }, - { - value: '8', - label: '清算', - }, - ], - type: 'string', - 'x-component': 'Radio.Group', - title: '公司状态(单选)', - }, - }, - { - key: 'x8v9qf95amt', - name: 'range_check', - type: 'array', - interface: 'checkboxGroup', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - enum: [ - { - value: 'F3134', - label: '软件销售', - }, - { - value: 'I3006', - label: '软件开发', - }, - { - value: 'I3007', - label: '人工智能基础软件开发', - }, - { - value: 'I3008', - label: '人工智能应用软件开发', - }, - { - value: 'I3010', - label: '软件外包服务', - }, - { - value: 'I3011', - label: '网络与信息安全软件开发', - }, - { - value: 'I3012', - label: '人工智能理论与算法软件开发', - }, - { - value: 'I3014', - label: '数字文化创意软件开发', - }, - { - value: 'I3027', - label: '信息技术咨询服务', - }, - { - value: 'I3032', - label: '数据处理服务', - }, - { - value: 'I3034', - label: '计算机系统服务', - }, - { - value: 'L2032', - label: '信息咨询服务(不含许可类信息咨询服务)', - }, - { - value: 'L2095', - label: '企业管理咨询', - }, - { - value: 'M2070', - label: '技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广', - }, - { - value: 'O3010', - label: '咨询策划服务', - }, - { - value: 'P1029', - label: '业务培训(不含教育培训、职业技能培训等需取得许可的培训)', - }, - ], - type: 'string', - 'x-component': 'Checkbox.Group', - title: '经营范围(复选)', - }, - defaultValue: [], - }, - { - key: 'c50l1zguamt', - name: 'range_markdown', - type: 'text', - interface: 'markdown', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'Markdown', - title: '经营范围(Markdown)', - }, - }, - { - key: 'msj1qa4damt', - name: 'range_richtext', - type: 'text', - interface: 'richText', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - 'x-component': 'RichText', - title: '经营范围(富文本)', - }, - }, - { - key: 'q755hkxmamt', - name: 'establishdate', - type: 'date', - interface: 'datetime', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - 'x-component-props': { - dateFormat: 'YYYY-MM-DD', - gmt: false, - showTime: false, - }, - type: 'string', - 'x-component': 'DatePicker', - title: '成立日期(日期)', - }, - }, - { - key: '9kn4ujeramt', - name: 'range_json', - type: 'json', - interface: 'json', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - defaultValue: null, - uiSchema: { - type: 'object', - 'x-component': 'Input.JSON', - 'x-component-props': { - autoSize: { - minRows: 5, - }, - }, - default: null, - title: '经营范围(JSON)', - }, - }, - { - key: 'vqcsj7htamt', - name: 'url', - type: 'string', - interface: 'url', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'string', - title: '官网地址(URL)', - 'x-component': 'Input.URL', - }, - }, - { - key: '0naiw2nq4j0', - name: 'insuranceratio_formula', - type: 'formula', - interface: 'formula', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - dataType: 'double', - uiSchema: { - 'x-component-props': { - step: '0.01', - stringMode: true, - }, - type: 'number', - 'x-component': 'Formula.Result', - 'x-read-pretty': true, - title: '参保占比(公式)', - }, - engine: 'math.js', - expression: '100*{{insurednum}}/{{staffnum}}', - }, - { - key: 'fd9sqp8usoj', - name: 'sharesnum', - type: 'bigInt', - interface: 'integer', - collectionName: 'tt_amt_org', - parentKey: null, - reverseKey: null, - uiSchema: { - type: 'number', - 'x-component': 'InputNumber', - 'x-component-props': { - stringMode: true, - step: '1', - }, - 'x-validator': 'integer', - title: '股票数量(整数)', - }, - }, - ], - category: [], - logging: true, - autoGenId: true, - createdBy: true, - updatedBy: true, - createdAt: true, - updatedAt: true, - sortable: true, - template: 'general', - }, - ], -}; - -export default module.exports = { - appendJsonCollectionName, - builtin_General, - builtin_File, - builtin_Expression, - e2e_GeneralFormsTable, - generateRandomLetters, -}; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/workFlow.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/workFlow.ts deleted file mode 100644 index 88503b8f0..000000000 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/pageobject/workFlow.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Locator, Page } from '@nocobase/test/e2e'; - -export class CreateWorkFlow { - readonly page: Page; - name: Locator; - triggerType: Locator; - description: Locator; - autoDeleteHistory: Locator; - - constructor(page: Page) { - this.page = page; - this.name = page.getByLabel('block-item-CollectionField-workflows-Name').getByRole('textbox'); - this.triggerType = page.getByTestId('select-single'); - this.description = page.getByTestId('description-item').getByRole('textbox'); - this.autoDeleteHistory = page.getByTestId('select-multiple'); - } -} - -export class EditWorkFlow { - readonly page: Page; - name: Locator; - statusIsOn: Locator; - statusIisOff: Locator; - description: Locator; - autoDeleteHistory: Locator; - - constructor(page: Page) { - this.page = page; - this.name = page.getByTestId('title-item').getByRole('textbox'); - this.statusIsOn = page.getByTestId('enabled-item').getByLabel('On'); - this.statusIisOff = page.getByLabel('Off'); - this.description = page.getByTestId('description-item').getByRole('textbox'); - this.autoDeleteHistory = page - .getByTestId('deleteExecutionOnStatus-item') - .getByTestId('antd-select') - .locator('div') - .nth(1); - } -} diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/triggerCollectionEvent.test.ts b/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/triggerCollectionEvent.test.ts deleted file mode 100644 index 8c6b29340..000000000 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/__tests__/e2e/triggerCollectionEvent.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { expect, test } from '@nocobase/test/e2e'; -import { dayjs } from '@nocobase/utils'; -import { appendJsonCollectionName, e2e_GeneralFormsTable, generateRandomLetters } from './pageobject/e2eTemplateJson'; -import { CreateWorkFlow } from './pageobject/workFlow'; - -test.describe('trigger collection events', () => { - test('add data to trigger collection events', async ({ page, mockPage }) => { - //用例编号 - const caseNum = 'WF20AA'; - //后缀标识,用于不同用例调用e2eTemplateJson.ts中相同模板JSON生成不同的数据表标识、名称 - const appendText = caseNum.toLowerCase() + generateRandomLetters().toString(); - //用例标题 - const caseTitle = 'add data to trigger collection events'; - - // 1、前置条件:1.1、已登录;1.2、存在一个配置好数据表的表单事件工作流;1.3、存在一个添加数据的区块 - //创建数据表 - const e2eJsonCollectionDisplayName = '自动>组织[普通表]'; - const e2eJsonCollectionName = 'tt_amt_org'; - - const collectionDisplayName = e2eJsonCollectionDisplayName + appendText; - const collectionName = e2eJsonCollectionName + appendText; - const fieldName = 'orgname'; - const fieldDisplayName = '公司名称(单行文本)'; - - const newPage = mockPage(appendJsonCollectionName(e2e_GeneralFormsTable, appendText)); - //配置工作流 - await page.goto('/admin/settings/workflow'); - await page.waitForLoadState('networkidle'); - await page.getByLabel('action-Action-Add new-workflows').click(); - const createWorkFlow = new CreateWorkFlow(page); - const workFlowName = caseTitle + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); - await createWorkFlow.name.fill(workFlowName); - await createWorkFlow.triggerType.click(); - await page.getByTitle('Collection event').getByText('Collection event').click(); - await page.getByLabel('action-Action-Submit-workflows').click(); - await expect(page.getByText(workFlowName)).toBeVisible(); - - //配置工作流触发器 - await page.getByLabel(`action-WorkflowLink-Configure-workflows-${workFlowName}`).click(); - await page.getByRole('button', { name: 'Configure' }).click(); - await page.getByTestId('select-collection').click(); - await page.getByText(collectionDisplayName).click(); - await page.getByTestId('select-single').click(); - await page.getByText('After record added', { exact: true }).click(); - await page.getByLabel('action-Action-Submit-workflows').click(); - await page.getByRole('link', { name: 'Workflow' }).click(); - await page.getByLabel(`action-Action.Link-Edit-workflows-${workFlowName}`).click(); - await page.getByLabel('On', { exact: true }).check(); - await page.getByLabel('action-Action-Submit-workflows').click(); - - //配置录入数据区块 - await newPage.goto(); - await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover(); - await page.getByRole('menuitem', { name: 'table Table' }).hover(); - await page.getByRole('menuitem', { name: collectionDisplayName }).click(); - - // 移开鼠标,关闭菜单 - await page.mouse.move(300, 0); - - await page.getByText('Configure columns').hover(); - await page.getByText(fieldDisplayName).click(); - await page.getByText('Configure actions').hover(); - await page.getByRole('menuitem', { name: 'Add new' }).click(); - await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeEnabled(); - - await page.getByLabel(`action-Action-Add new-create-tt_amt_org${appendText}-table`).click(); - await page.getByLabel(`schema-initializer-Grid-CreateFormBlockInitializers-tt_amt_org${appendText}`).hover(); - await page.getByRole('menuitem', { name: 'form Form' }).click(); - - // 移开鼠标,关闭菜单 - await page.mouse.move(300, 0); - - await page.getByLabel(`schema-initializer-ActionBar-CreateFormActionInitializers-tt_amt_org${appendText}`).hover(); - await page.getByRole('menuitem', { name: 'Submit' }).click(); - await page.getByLabel(`schema-initializer-Grid-FormItemInitializers-tt_amt_org${appendText}`).hover(); - await page.getByRole('menuitem', { name: fieldDisplayName }).click(); - - await page.mouse.move(300, 0); - - // 2、测试步骤:进入“数据区块”-“添加”按钮,填写表单,点击“确定”按钮 - const fieldData = fieldDisplayName + dayjs().format('YYYYMMDDHHmmss.SSS').toString(); - await page.getByRole('textbox').fill(fieldData); - await page.getByLabel(`action-Action-Submit-submit-${collectionName}-form`, { exact: true }).click(); - await expect(page.getByText(fieldData)).toBeVisible(); - - // 3、预期结果:数据添加成功,工作流成功触发 - await expect(page.getByText(fieldData)).toBeVisible(); - await page.goto('/admin/settings/workflow'); - await expect(page.getByRole('table').locator('a').filter({ hasText: '1' })).toBeVisible(); - - // 4、后置处理:删除工作流 - await page.getByLabel('action-Filter.Action-Filter-filter-workflows').click(); - await page.getByRole('textbox').fill(workFlowName); - await page.getByRole('button', { name: 'Submit' }).click(); - await page.getByLabel(`action-Action.Link-Delete-workflows-${workFlowName}`).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect(page.getByText(workFlowName)).toBeHidden(); - }); -}); diff --git a/yarn.lock b/yarn.lock index b5621bea3..bed847086 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19128,6 +19128,13 @@ osx-release@^1.0.0: dependencies: minimist "^1.1.0" +p-all@3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb" + integrity sha512-qUZbvbBFVXm6uJ7U/WDiO0fv6waBMbjlCm4E66oZdRR+egswICarIdHyVSZZHudH8T5SF8x/JG0q0duFzPnlBw== + dependencies: + p-map "^4.0.0" + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"