feat(workflow): 移除工作流操作类型的触发事件 (#1561)
Reviewed-on: daoyoucloud/tachybase#1561 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
569dbf2423
commit
04cd8135ca
@ -4,7 +4,6 @@ import { Registry } from '@tachybase/utils/client';
|
||||
|
||||
import { WorkflowLink } from './components/WorkflowLink';
|
||||
import { ExecutionPage } from './ExecutionPage';
|
||||
import { PluginActionTrigger } from './features/action-trigger';
|
||||
import { PluginAggregate } from './features/aggregate';
|
||||
import PluginApproval from './features/approval';
|
||||
import PluginWorkflowDataMappingClient from './features/data-mapping';
|
||||
@ -86,7 +85,6 @@ export class PluginWorkflow extends Plugin {
|
||||
await this.pm.add(PluginDaynamicCalculation);
|
||||
await this.pm.add(PluginDelay);
|
||||
await this.pm.add(PluginAggregate);
|
||||
await this.pm.add(PluginActionTrigger);
|
||||
await this.pm.add(PluginWorkflowJsonParseClient);
|
||||
await this.pm.add(PluginWorkflowJSParseClient);
|
||||
await this.pm.add(PluginWorkflowDataMappingClient);
|
||||
|
@ -1,134 +0,0 @@
|
||||
import {
|
||||
SchemaInitializerItemType,
|
||||
useCollectionDataSource,
|
||||
useCollectionManager_deprecated,
|
||||
useCompile,
|
||||
} from '@tachybase/client';
|
||||
import { useForm } from '@tachybase/schema';
|
||||
|
||||
import { CollectionBlockInitializer } from '../../components';
|
||||
import { useWorkflowAnyExecuted } from '../../hooks';
|
||||
import { lang, NAMESPACE, tval } from '../../locale';
|
||||
import { Trigger } from '../../triggers';
|
||||
import { getCollectionFieldOptions } from '../../variable';
|
||||
|
||||
export class ActionTrigger extends Trigger {
|
||||
title = tval('Action event(deprecated)');
|
||||
description = `{{t("Triggers after specific operations on data are submitted, such as create, update, delete, etc., or directly submitting a record to the workflow.", { ns: "${NAMESPACE}" })}}`;
|
||||
fieldset = {
|
||||
collection: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'DataSourceCollectionCascader',
|
||||
'x-disabled': '{{ useWorkflowAnyExecuted() }}',
|
||||
title: `{{t("Collection", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Which collection record belongs to.", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-reactions': [
|
||||
{
|
||||
target: 'appends',
|
||||
effects: ['onFieldValueChange'],
|
||||
fulfill: {
|
||||
state: {
|
||||
value: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
appends: {
|
||||
type: 'array',
|
||||
title: `{{t("Associations to use", { ns: "${NAMESPACE}" })}}`,
|
||||
description: `{{t("Please select the associated fields that need to be accessed in subsequent nodes. With more than two levels of to-many associations may cause performance issue, please use with caution.", { ns: "workflow" })}}`,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'AppendsTreeSelect',
|
||||
'x-component-props': {
|
||||
title: 'Preload associations',
|
||||
multiple: true,
|
||||
useCollection() {
|
||||
const { values } = useForm();
|
||||
return values?.collection;
|
||||
},
|
||||
},
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['collection'],
|
||||
fulfill: {
|
||||
state: {
|
||||
visible: '{{!!$deps[0]}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
scope = {
|
||||
useCollectionDataSource,
|
||||
useWorkflowAnyExecuted,
|
||||
};
|
||||
isActionTriggerable = (config, context) => {
|
||||
return ['create', 'update', 'customize:update', 'customize:triggerWorkflows'].includes(context.action);
|
||||
};
|
||||
useVariables(config, options) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const compile = useCompile();
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { getCollectionFields } = useCollectionManager_deprecated();
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const langTriggerData = lang('Trigger data');
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const langUserSubmittedForm = lang('User submitted action');
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const langRoleSubmittedForm = lang('Role of user submitted action');
|
||||
const rootFields = [
|
||||
{
|
||||
collectionName: config.collection,
|
||||
name: 'data',
|
||||
type: 'hasOne',
|
||||
target: config.collection,
|
||||
uiSchema: {
|
||||
title: langTriggerData,
|
||||
},
|
||||
},
|
||||
{
|
||||
collectionName: 'users',
|
||||
name: 'user',
|
||||
type: 'hasOne',
|
||||
target: 'users',
|
||||
uiSchema: {
|
||||
title: langUserSubmittedForm,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'roleName',
|
||||
uiSchema: {
|
||||
title: langRoleSubmittedForm,
|
||||
},
|
||||
},
|
||||
];
|
||||
const result = getCollectionFieldOptions({
|
||||
// depth,
|
||||
appends: ['data', 'user', ...(config.appends?.map((item) => `data.${item}`) || [])],
|
||||
...options,
|
||||
fields: rootFields,
|
||||
compile,
|
||||
getCollectionFields,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
useInitializers(config): SchemaInitializerItemType | null {
|
||||
if (!config.collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'triggerData',
|
||||
type: 'item',
|
||||
key: 'triggerData',
|
||||
title: `{{t("Trigger data", { ns: "${NAMESPACE}" })}}`,
|
||||
Component: CollectionBlockInitializer,
|
||||
collection: config.collection,
|
||||
dataPath: '$context.data',
|
||||
};
|
||||
}
|
||||
}
|
@ -1,676 +0,0 @@
|
||||
import {
|
||||
apiCreateRecordTriggerFormEvent,
|
||||
apiCreateWorkflow,
|
||||
apiDeleteWorkflow,
|
||||
apiGetDataSourceCount,
|
||||
apiGetWorkflow,
|
||||
apiUpdateWorkflowTrigger,
|
||||
appendJsonCollectionName,
|
||||
FormEventTriggerNode,
|
||||
generalWithNoRelationalFields,
|
||||
WorkflowListRecords,
|
||||
} from '@tachybase/plugin-workflow-test/e2e';
|
||||
import { expect, test } from '@tachybase/test/e2e';
|
||||
import { dayjs } from '@tachybase/utils';
|
||||
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
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: 'action',
|
||||
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('menuitemcheckbox', { name: 'Main right' }).click();
|
||||
await page.getByRole('menuitemcheckbox', { name: triggerNodeCollectionDisplayName }).click();
|
||||
await formEventTriggerNode.submitButton.click();
|
||||
|
||||
//配置录入数据区块
|
||||
const newPage = mockPage();
|
||||
await newPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
||||
await page.getByRole('menuitem', { name: 'table Table' }).hover();
|
||||
const dataSourcesCount = await apiGetDataSourceCount();
|
||||
if (dataSourcesCount > 1) {
|
||||
await page.getByRole('menuitem', { name: 'Main right' }).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-popup:addNew:addBlock-${triggerNodeCollectionName}`).hover();
|
||||
await page.getByRole('menuitem', { name: 'form Form' }).click();
|
||||
// 移开鼠标,关闭菜单
|
||||
await page.mouse.move(300, 0);
|
||||
await page
|
||||
.getByLabel(`schema-initializer-ActionBar-createForm:configureActions-${triggerNodeCollectionName}`)
|
||||
.hover();
|
||||
await page.getByRole('menuitem', { name: 'Submit' }).click();
|
||||
// 绑定工作流
|
||||
await page.getByLabel(`schema-initializer-Grid-form:configureFields-${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-actionSettings:createSubmit-${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: 'Submit', 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: 'action',
|
||||
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('menuitemcheckbox', { name: 'Main right' }).click();
|
||||
await page.getByRole('menuitemcheckbox', { name: triggerNodeCollectionDisplayName }).click();
|
||||
await formEventTriggerNode.submitButton.click();
|
||||
|
||||
//配置录入数据区块
|
||||
const newPage = mockPage();
|
||||
await newPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.getByLabel('schema-initializer-Grid-page:addBlock').hover();
|
||||
await page.getByRole('menuitem', { name: 'table Table' }).hover();
|
||||
const dataSourcesCount = await apiGetDataSourceCount();
|
||||
if (dataSourcesCount > 1) {
|
||||
await page.getByRole('menuitem', { name: 'Main right' }).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-popup:addNew:addBlock-${triggerNodeCollectionName}`).hover();
|
||||
await page.getByRole('menuitem', { name: 'form Form' }).click();
|
||||
|
||||
// 移开鼠标,关闭菜单
|
||||
await page.mouse.move(300, 0);
|
||||
|
||||
// 绑定工作流
|
||||
await page
|
||||
.getByLabel(`schema-initializer-ActionBar-createForm:configureActions-${triggerNodeCollectionName}`)
|
||||
.hover();
|
||||
await page.getByRole('menuitem', { name: 'Customize' }).hover();
|
||||
await page.getByRole('menuitem', { name: 'Submit to workflow' }).click();
|
||||
await page.getByLabel(`schema-initializer-Grid-form:configureFields-${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: 'Submit', 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('Action 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: 'action',
|
||||
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('Action 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: 'action',
|
||||
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('Action 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: 'action',
|
||||
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('Action 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: 'action',
|
||||
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('Action 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: 'action',
|
||||
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('Action 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: 'action',
|
||||
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 action 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: 'action',
|
||||
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
|
||||
.getByLabel('block-item-DataSourceCollectionCascader-workflows-Collection')
|
||||
.getByText(`Main / ${triggerNodeCollectionDisplayName}`),
|
||||
).toBeVisible();
|
||||
// 4、后置处理:删除工作流
|
||||
await apiDeleteWorkflow(workflowId);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Configuration page delete version', () => {});
|
||||
|
||||
test.describe('Node Add Modify Delete', () => {});
|
@ -1,179 +0,0 @@
|
||||
import {
|
||||
apiCreateRecordTriggerFormEvent,
|
||||
apiCreateWorkflow,
|
||||
apiDeleteWorkflow,
|
||||
apiGetWorkflow,
|
||||
apiUpdateWorkflowTrigger,
|
||||
appendJsonCollectionName,
|
||||
CreateWorkFlow,
|
||||
EditWorkFlow,
|
||||
FormEventTriggerNode,
|
||||
generalWithNoRelationalFields,
|
||||
WorkflowListRecords,
|
||||
} from '@tachybase/plugin-workflow-test/e2e';
|
||||
import { expect, test } from '@tachybase/test/e2e';
|
||||
import { dayjs } from '@tachybase/utils';
|
||||
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
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: 'action',
|
||||
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 Action 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: 'Action 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 Action 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: 'action',
|
||||
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 Action 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: 'action',
|
||||
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 Action 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: 'action',
|
||||
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 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 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', () => {});
|
@ -1,92 +0,0 @@
|
||||
import { Plugin, SchemaInitializerItemType } from '@tachybase/client';
|
||||
|
||||
import WorkflowPlugin, { useRecordTriggerWorkflowsActionProps, useTriggerWorkflowsActionProps } from '../..';
|
||||
import { ActionTrigger } from './ActionTrigger';
|
||||
|
||||
const submitToWorkflowActionInitializer: SchemaInitializerItemType = {
|
||||
name: 'submitToWorkflow',
|
||||
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
||||
Component: 'CustomizeActionInitializer',
|
||||
schema: {
|
||||
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
||||
'x-component': 'Action',
|
||||
'x-use-component-props': 'useTriggerWorkflowsActionProps',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-action-settings': {
|
||||
// assignedValues: {},
|
||||
skipValidator: false,
|
||||
onSuccess: {
|
||||
manualClose: true,
|
||||
redirecting: false,
|
||||
successMessage: '{{t("Submitted successfully")}}',
|
||||
},
|
||||
triggerWorkflows: [],
|
||||
},
|
||||
'x-action': 'customize:triggerWorkflows',
|
||||
},
|
||||
};
|
||||
|
||||
const recordTriggerWorkflowActionInitializer: SchemaInitializerItemType = {
|
||||
name: 'submitToWorkflow',
|
||||
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
||||
Component: 'CustomizeActionInitializer',
|
||||
schema: {
|
||||
title: '{{t("Submit to workflow", { ns: "workflow" })}}',
|
||||
'x-component': 'Action',
|
||||
'x-use-component-props': 'useRecordTriggerWorkflowsActionProps',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-action-settings': {
|
||||
onSuccess: {
|
||||
manualClose: true,
|
||||
redirecting: false,
|
||||
successMessage: '{{t("Submitted successfully")}}',
|
||||
},
|
||||
triggerWorkflows: [],
|
||||
},
|
||||
'x-action': 'customize:triggerWorkflows',
|
||||
},
|
||||
};
|
||||
|
||||
const recordTriggerWorkflowActionLinkInitializer = {
|
||||
...recordTriggerWorkflowActionInitializer,
|
||||
schema: {
|
||||
...recordTriggerWorkflowActionInitializer.schema,
|
||||
'x-component': 'Action.Link',
|
||||
},
|
||||
};
|
||||
|
||||
export class PluginActionTrigger extends Plugin {
|
||||
async load() {
|
||||
const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
|
||||
workflow.registerTrigger('action', ActionTrigger);
|
||||
|
||||
this.app.addScopes({
|
||||
useTriggerWorkflowsActionProps,
|
||||
useRecordTriggerWorkflowsActionProps,
|
||||
});
|
||||
|
||||
const FormActionInitializers = this.app.schemaInitializerManager.get('FormActionInitializers');
|
||||
FormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
|
||||
|
||||
const CreateFormActionInitializers = this.app.schemaInitializerManager.get('createForm:configureActions');
|
||||
CreateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
|
||||
|
||||
const UpdateFormActionInitializers = this.app.schemaInitializerManager.get('editForm:configureActions');
|
||||
UpdateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
|
||||
|
||||
const DetailsActionInitializers = this.app.schemaInitializerManager.get('detailsWithPaging:configureActions');
|
||||
DetailsActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer);
|
||||
|
||||
const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('details:configureActions');
|
||||
ReadPrettyFormActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer);
|
||||
|
||||
const TableActionColumnInitializers = this.app.schemaInitializerManager.get('table:configureItemActions');
|
||||
TableActionColumnInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
|
||||
|
||||
const GridCardItemActionInitializers = this.app.schemaInitializerManager.get('gridCard:configureItemActions');
|
||||
GridCardItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
|
||||
|
||||
const ListItemActionInitializers = this.app.schemaInitializerManager.get('list:configureItemActions');
|
||||
ListItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@
|
||||
"Workflow will be triggered after submitting succeeded.": "제출 성공 후 워크플로우가 트리거됩니다.",
|
||||
"Workflow will be triggered after saving succeeded.": "저장 성공 후 워크플로우가 트리거됩니다.",
|
||||
"Workflow will be triggered directly once the button is clicked, without data saving.": "버튼을 클릭하면 데이터 저장 없이 워크플로우가 직접 트리거됩니다.",
|
||||
"Submit to workflow": "워크플로우에 제출",
|
||||
"Add workflow": "워크플로우 추가",
|
||||
"Select workflow": "워크플로우 선택",
|
||||
"Trigger data context": "트리거 데이터 컨텍스트",
|
||||
|
@ -281,7 +281,6 @@
|
||||
"Status": "状态",
|
||||
"String operation": "字符串",
|
||||
"Submission may be withdrawn, please try refresh the list.": "提交可能已被撤回,请尝试刷新列表。",
|
||||
"Submit to workflow": "提交至工作流",
|
||||
"Submitted": "提交",
|
||||
"Succeed and continue": "通过并继续",
|
||||
"Succeeded": "成功",
|
||||
|
@ -8,9 +8,7 @@ import LRUCache from 'lru-cache';
|
||||
|
||||
import initActions from './actions';
|
||||
import { EXECUTION_STATUS } from './constants';
|
||||
import { PluginActionTrigger } from './features/action-trigger/Plugin';
|
||||
import { PluginAggregate } from './features/aggregate/Plugin';
|
||||
import PluginWorkflowAPIRegularServer from './features/api-regular/plugin';
|
||||
import PluginWorkflowApproval from './features/approval';
|
||||
import PluginWorkflowDataMappingServer from './features/data-mapping/plugin';
|
||||
import { PluginDelay } from './features/delay/Plugin';
|
||||
@ -75,7 +73,6 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
this.addFeature(PluginDynamicCalculation);
|
||||
this.addFeature(PluginDelay);
|
||||
this.addFeature(PluginAggregate);
|
||||
this.addFeature(PluginActionTrigger);
|
||||
this.addFeature(PluginWorkflowJSONParseServer);
|
||||
this.addFeature(PluginWorkflowJSParseServer);
|
||||
this.addFeature(PluginWorkflowDataMappingServer);
|
||||
|
@ -1,156 +0,0 @@
|
||||
import { Context as ActionContext, Next } from '@tachybase/actions';
|
||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||
import { Model, modelAssociationByKey } from '@tachybase/database';
|
||||
import Application, { DefaultContext } from '@tachybase/server';
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { BelongsTo, HasOne } from 'sequelize';
|
||||
|
||||
import WorkflowPlugin, { toJSON, Trigger, WorkflowModel } from '../..';
|
||||
|
||||
interface Context extends ActionContext, DefaultContext {}
|
||||
|
||||
export default class extends Trigger {
|
||||
constructor(workflow: WorkflowPlugin) {
|
||||
super(workflow);
|
||||
|
||||
workflow.app.use(this.middleware, { after: 'dataSource' });
|
||||
}
|
||||
|
||||
async triggerAction(context: Context, next: Next) {
|
||||
const { triggerWorkflows } = context.action.params;
|
||||
|
||||
if (!triggerWorkflows) {
|
||||
return context.throw(400);
|
||||
}
|
||||
|
||||
context.status = 202;
|
||||
await next();
|
||||
|
||||
this.trigger(context);
|
||||
}
|
||||
|
||||
middleware = async (context: Context, next: Next) => {
|
||||
const {
|
||||
resourceName,
|
||||
actionName,
|
||||
params: { triggerWorkflows },
|
||||
} = context.action;
|
||||
|
||||
if (resourceName === 'workflows' && actionName === 'trigger') {
|
||||
return this.triggerAction(context, next);
|
||||
}
|
||||
|
||||
await next();
|
||||
|
||||
if (!triggerWorkflows) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!['create', 'update'].includes(actionName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.trigger(context);
|
||||
};
|
||||
|
||||
private async trigger(context: Context) {
|
||||
const { triggerWorkflows = '', values } = context.action.params;
|
||||
const dataSourceHeader = context.get('x-data-source') || 'main';
|
||||
|
||||
const { currentUser, currentRole } = context.state;
|
||||
const { model: UserModel } = this.workflow.db.getCollection('users');
|
||||
const userInfo = {
|
||||
user: UserModel.build(currentUser).desensitize(),
|
||||
roleName: currentRole,
|
||||
};
|
||||
|
||||
const triggers = triggerWorkflows.split(',').map((trigger) => trigger.split('!'));
|
||||
const workflowRepo = this.workflow.db.getRepository('workflows');
|
||||
const workflows = (
|
||||
await workflowRepo.find({
|
||||
filter: {
|
||||
key: triggers.map((trigger) => trigger[0]),
|
||||
current: true,
|
||||
type: 'action',
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
).filter((workflow) => Boolean(workflow.config.collection));
|
||||
const syncGroup = [];
|
||||
const asyncGroup = [];
|
||||
for (const workflow of workflows) {
|
||||
const { collection, appends = [] } = workflow.config;
|
||||
const [dataSourceName, collectionName] = parseCollectionName(collection);
|
||||
const trigger = triggers.find((trigger) => trigger[0] == workflow.key);
|
||||
const event = [workflow];
|
||||
if (context.action.resourceName !== 'workflows') {
|
||||
if (!context.body) {
|
||||
continue;
|
||||
}
|
||||
if (dataSourceName !== dataSourceHeader) {
|
||||
continue;
|
||||
}
|
||||
const { body: data } = context;
|
||||
for (const row of Array.isArray(data) ? data : [data]) {
|
||||
let payload = row;
|
||||
if (trigger[1]) {
|
||||
const paths = trigger[1].split('.');
|
||||
for (const field of paths) {
|
||||
if (payload.get(field)) {
|
||||
payload = payload.get(field);
|
||||
} else {
|
||||
const association = <HasOne | BelongsTo>modelAssociationByKey(payload, field);
|
||||
payload = await payload[association.accessors.get]();
|
||||
}
|
||||
}
|
||||
}
|
||||
const model = payload.constructor;
|
||||
if (payload instanceof Model) {
|
||||
if (collectionName !== model.collection.name) {
|
||||
continue;
|
||||
}
|
||||
if (appends.length) {
|
||||
payload = await model.collection.repository.findOne({
|
||||
filterByTk: payload.get(model.primaryKeyAttribute),
|
||||
appends,
|
||||
});
|
||||
}
|
||||
}
|
||||
// this.workflow.trigger(workflow, { data: toJSON(payload), ...userInfo });
|
||||
event.push({ data: toJSON(payload), ...userInfo });
|
||||
}
|
||||
} else {
|
||||
const { model, repository } = (<Application>context.app).dataSourceManager.dataSources
|
||||
.get(dataSourceName)
|
||||
.collectionManager.getCollection(collectionName);
|
||||
let data = trigger[1] ? get(values, trigger[1]) : values;
|
||||
const pk = get(data, model.primaryKeyAttribute);
|
||||
if (appends.length && pk != null) {
|
||||
data = await repository.findOne({
|
||||
filterByTk: pk,
|
||||
appends,
|
||||
});
|
||||
}
|
||||
// this.workflow.trigger(workflow, {
|
||||
// data,
|
||||
// ...userInfo,
|
||||
// });
|
||||
event.push({ data, ...userInfo });
|
||||
}
|
||||
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
||||
}
|
||||
|
||||
for (const event of syncGroup) {
|
||||
await this.workflow.trigger(event[0], event[1], { httpContext: context });
|
||||
}
|
||||
|
||||
for (const event of asyncGroup) {
|
||||
this.workflow.trigger(event[0], event[1]);
|
||||
}
|
||||
}
|
||||
|
||||
on(workflow: WorkflowModel) {}
|
||||
|
||||
off(workflow: WorkflowModel) {}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { Plugin } from '@tachybase/server';
|
||||
|
||||
import WorkflowPlugin from '../..';
|
||||
import ActionTrigger from './ActionTrigger';
|
||||
|
||||
export class PluginActionTrigger extends Plugin {
|
||||
async load() {
|
||||
const workflowPlugin = this.app.pm.get(WorkflowPlugin) as WorkflowPlugin;
|
||||
workflowPlugin.registerTrigger('action', new ActionTrigger(workflowPlugin));
|
||||
}
|
||||
}
|
@ -1,619 +0,0 @@
|
||||
import Database from '@tachybase/database';
|
||||
import { EXECUTION_STATUS } from '@tachybase/plugin-workflow';
|
||||
import { getApp, sleep } from '@tachybase/plugin-workflow-test';
|
||||
import { MockServer } from '@tachybase/test';
|
||||
|
||||
import Plugin from '..';
|
||||
|
||||
describe('workflow > action-trigger', () => {
|
||||
let app: MockServer;
|
||||
let db: Database;
|
||||
let agent;
|
||||
let PostRepo;
|
||||
let CommentRepo;
|
||||
let WorkflowModel;
|
||||
let UserRepo;
|
||||
let users;
|
||||
let userAgents;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await getApp({
|
||||
plugins: ['users', 'auth', Plugin],
|
||||
});
|
||||
await app.pm.get('auth').install();
|
||||
agent = app.agent();
|
||||
db = app.db;
|
||||
WorkflowModel = db.getCollection('workflows').model;
|
||||
PostRepo = db.getCollection('posts').repository;
|
||||
CommentRepo = db.getCollection('comments').repository;
|
||||
UserRepo = db.getCollection('users').repository;
|
||||
|
||||
users = await UserRepo.create({
|
||||
values: [
|
||||
{ id: 2, nickname: 'a', roles: [{ name: 'root' }] },
|
||||
{ id: 3, nickname: 'b' },
|
||||
],
|
||||
});
|
||||
|
||||
userAgents = users.map((user) => app.agent().login(user));
|
||||
});
|
||||
|
||||
afterEach(() => app.destroy());
|
||||
|
||||
describe('create', () => {
|
||||
it('enabled / disabled', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(1);
|
||||
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1[0].context.data).toMatchObject({ title: 't1' });
|
||||
|
||||
await workflow.update({
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
const res2 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't2' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e2 = await workflow.getExecutions({ order: [['id', 'ASC']] });
|
||||
expect(e2.length).toBe(1);
|
||||
|
||||
await workflow.update({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const res3 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't3' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e3 = await workflow.getExecutions({ order: [['id', 'ASC']] });
|
||||
expect(e3.length).toBe(2);
|
||||
expect(e3[1].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e3[1].context.data).toMatchObject({ title: 't3' });
|
||||
});
|
||||
|
||||
it('only trigger if params provided matching collection config', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(1);
|
||||
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1[0].context.data).toMatchObject({ title: 't1' });
|
||||
|
||||
await workflow.update({
|
||||
config: {
|
||||
collection: 'comments',
|
||||
},
|
||||
});
|
||||
|
||||
const res2 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't2' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e2 = await workflow.getExecutions({ order: [['id', 'ASC']] });
|
||||
expect(e2.length).toBe(1);
|
||||
// expect(e2[1].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
// expect(e2[1].context.data).toMatchObject({ title: 't2' });
|
||||
});
|
||||
|
||||
it('system fields could be accessed', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(1);
|
||||
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1[0].context.data).toHaveProperty('createdAt');
|
||||
});
|
||||
|
||||
it('appends', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
appends: ['createdBy'],
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(1);
|
||||
expect(e1[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1[0].context.data).toHaveProperty('createdBy');
|
||||
});
|
||||
|
||||
it('user submitted form', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.user).toBeDefined();
|
||||
expect(e1.context.user.id).toBe(users[0].id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
it('trigger after updated', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
appends: ['createdBy'],
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(0);
|
||||
|
||||
const res2 = await userAgents[0].resource('posts').update({
|
||||
filterByTk: res1.body.data.id,
|
||||
values: { title: 't2' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e2] = await workflow.getExecutions();
|
||||
expect(e2.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e2.context.data).toHaveProperty('title', 't2');
|
||||
expect(e2.context.data).toHaveProperty('createdBy');
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('destroy', () => {
|
||||
it('trigger after destroyed', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const p1 = await PostRepo.create({
|
||||
values: { title: 't1' },
|
||||
});
|
||||
const p2 = await PostRepo.create({
|
||||
values: { title: 't2' },
|
||||
});
|
||||
const res1 = await userAgents[0].resource('posts').destroy({
|
||||
filterByTk: p1.id,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1 = await workflow.getExecutions();
|
||||
expect(e1.length).toBe(0);
|
||||
|
||||
const res2 = await userAgents[0].resource('posts').destroy({
|
||||
filterByTk: p2.id,
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e2] = await workflow.getExecutions();
|
||||
expect(e2.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e2.context.data).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('directly trigger', () => {
|
||||
it('no collection configured should not be triggered', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1s = await workflow.getExecutions();
|
||||
expect(e1s.length).toBe(0);
|
||||
});
|
||||
|
||||
it('trigger on form data', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
appends: ['createdBy'],
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 't1' });
|
||||
expect(e1.context.data.createdBy).toBeUndefined();
|
||||
});
|
||||
|
||||
it('trigger on record data', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
appends: ['createdBy'],
|
||||
},
|
||||
});
|
||||
|
||||
const post = await PostRepo.create({
|
||||
values: { title: 't1', createdBy: users[0].id },
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: post.toJSON(),
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 't1' });
|
||||
expect(e1.context.data).toHaveProperty('createdBy');
|
||||
expect(e1.context.data.createdBy.id).toBe(users[0].id);
|
||||
});
|
||||
|
||||
it('multi trigger', async () => {
|
||||
const w1 = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const w2 = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${w1.key},${w2.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await w1.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 't1' });
|
||||
|
||||
const [e2] = await w2.getExecutions();
|
||||
expect(e2.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e2.context.data).toMatchObject({ title: 't1' });
|
||||
});
|
||||
|
||||
it('user submitted form', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
appends: ['createdBy'],
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.user).toBeDefined();
|
||||
expect(e1.context.user.id).toBe(users[0].id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('context data path', () => {
|
||||
it('level: 1', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't1', category: { title: 'c1' } },
|
||||
triggerWorkflows: `${workflow.key}!category`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 'c1' });
|
||||
});
|
||||
|
||||
it('level: 2', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { content: 'comment1', post: { category: { title: 'c1' } } },
|
||||
triggerWorkflows: `${workflow.key}!post.category`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 'c1' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('workflow key', () => {
|
||||
it('revision', async () => {
|
||||
const w1 = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${w1.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await w1.getExecutions();
|
||||
expect(e1.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e1.context.data).toMatchObject({ title: 't1' });
|
||||
|
||||
const res2 = await userAgents[0].resource('workflows').revision({
|
||||
filterByTk: w1.id,
|
||||
filter: {
|
||||
key: w1.key,
|
||||
},
|
||||
});
|
||||
const w2 = await WorkflowModel.findByPk(res2.body.data.id);
|
||||
await w2.update({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const res3 = await userAgents[0].resource('workflows').trigger({
|
||||
values: { title: 't2' },
|
||||
triggerWorkflows: `${w1.key}`,
|
||||
});
|
||||
expect(res3.status).toBe(202);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e2 = await w1.getExecutions();
|
||||
expect(e2.length).toBe(1);
|
||||
const e3 = await w2.getExecutions();
|
||||
expect(e3.length).toBe(1);
|
||||
expect(e3[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
expect(e3[0].context.data).toMatchObject({ title: 't2' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('sync', () => {
|
||||
it('sync form trigger', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
sync: true,
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
const executions = await workflow.getExecutions();
|
||||
expect(executions.length).toBe(1);
|
||||
expect(executions[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
});
|
||||
|
||||
it('sync and async will all be triggered in one action', async () => {
|
||||
const w1 = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const w2 = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
sync: true,
|
||||
config: {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: [w1.key, w2.key].join(),
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
const e1s = await w1.getExecutions();
|
||||
expect(e1s.length).toBe(0);
|
||||
|
||||
const e2s = await w2.getExecutions();
|
||||
expect(e2s.length).toBe(1);
|
||||
expect(e2s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e3s = await w1.getExecutions();
|
||||
expect(e3s.length).toBe(1);
|
||||
expect(e3s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple data source', () => {
|
||||
it('trigger on different data source', async () => {
|
||||
const workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
type: 'action',
|
||||
config: {
|
||||
collection: 'another:posts',
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await userAgents[0].resource('posts').create({
|
||||
values: { title: 't1' },
|
||||
triggerWorkflows: `${workflow.key}`,
|
||||
});
|
||||
expect(res1.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e1s = await workflow.getExecutions();
|
||||
expect(e1s.length).toBe(0);
|
||||
|
||||
// const res2 = await userAgents[0]
|
||||
// .set('x-data-source', 'another')
|
||||
// .resource('posts')
|
||||
// .create({
|
||||
// values: { title: 't2' },
|
||||
// triggerWorkflows: `${workflow.key}`,
|
||||
// });
|
||||
const res2 = await agent
|
||||
.login(users[0])
|
||||
.set('x-data-source', 'another')
|
||||
.post('/api/posts:create')
|
||||
.query({ triggerWorkflows: `${workflow.key}` })
|
||||
.send({ title: 't2' });
|
||||
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const e2s = await workflow.getExecutions();
|
||||
expect(e2s.length).toBe(1);
|
||||
expect(e2s[0].status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user