Merge branch 'next' into develop

This commit is contained in:
Zeke Zhang
2025-06-14 22:15:02 +08:00
31 changed files with 334 additions and 86 deletions
@@ -63,7 +63,7 @@ interface Props {
expandFlag?: boolean;
dragSortBy?: string;
association?: string;
enableIndexÏColumn?: boolean;
enableIndexColumn?: boolean;
}
const InternalTableBlockProvider = (props: Props) => {
@@ -77,7 +77,7 @@ const InternalTableBlockProvider = (props: Props) => {
fieldNames,
collection,
association,
enableIndexÏColumn,
enableIndexColumn,
} = props;
const field: any = useField();
const { resource, service } = useBlockRequestContext();
@@ -136,7 +136,7 @@ const InternalTableBlockProvider = (props: Props) => {
setExpandFlag: setExpandFlagValue,
heightProps,
association,
enableIndexÏColumn,
enableIndexColumn,
}),
[
allIncludesChildren,
@@ -153,7 +153,7 @@ const InternalTableBlockProvider = (props: Props) => {
setExpandFlagValue,
showIndex,
association,
enableIndexÏColumn,
enableIndexColumn,
],
);
@@ -52,6 +52,7 @@ test.describe('where table block can be added', () => {
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).waitFor({ state: 'detached' });
await page.getByRole('menuitem', { name: 'Table right' }).hover();
await page.waitForTimeout(300);
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'parentAssociationField' }).click();
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover();
@@ -751,6 +751,7 @@ test.describe('actions schema settings', () => {
await page
.getByRole('button', { name: 'designer-schema-settings-Action.Link-actionSettings:popup-general' })
.hover();
await page.waitForTimeout(300);
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
@@ -39,11 +39,11 @@ const enabledIndexColumn: SchemaSettingsItemType = {
const { dn } = useDesignable();
return {
title: t('Enable index column'),
checked: field.decoratorProps.enableIndexÏColumn !== false,
onChange: async (enableIndexÏColumn) => {
checked: field.decoratorProps.enableIndexColumn !== false,
onChange: async (enableIndexColumn) => {
field.decoratorProps = field.decoratorProps || {};
field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn;
fieldSchema['x-decorator-props'].enableIndexÏColumn = enableIndexÏColumn;
field.decoratorProps.enableIndexColumn = enableIndexColumn;
fieldSchema['x-decorator-props'].enableIndexColumn = enableIndexColumn;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
@@ -41,11 +41,11 @@ const enabledIndexColumn: SchemaSettingsItemType = {
const { dn } = useDesignable();
return {
title: t('Enable index column'),
checked: field.componentProps.enableIndexÏColumn !== false,
onChange: async (enableIndexÏColumn) => {
checked: field.componentProps.enableIndexColumn !== false,
onChange: async (enableIndexColumn) => {
field.componentProps = field.componentProps || {};
field.componentProps.enableIndexÏColumn = enableIndexÏColumn;
fieldSchema['x-component-props'].enableIndexÏColumn = enableIndexÏColumn;
field.componentProps.enableIndexColumn = enableIndexColumn;
fieldSchema['x-component-props'].enableIndexColumn = enableIndexColumn;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
@@ -11,7 +11,7 @@ import { observer, useField, useFieldSchema } from '@formily/react';
import { transformMultiColumnToSingleColumn } from '@nocobase/utils/client';
import { Select, Space } from 'antd';
import { differenceBy, unionBy } from 'lodash';
import React, { useContext, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import {
FormProvider,
PopupSettingsProvider,
@@ -135,6 +135,11 @@ export const InternalPicker = observer(
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
return filter;
};
useEffect(() => {
if (!value) {
setSelectedRows([]);
}
}, [value]);
const usePickActionProps = () => {
const { setVisible } = useActionContext();
const { multiple, selectedRows, onChange, options, collectionField } = useContext(RecordPickerContext);
@@ -107,7 +107,7 @@ export const SubTable: any = observer(
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
const recordV2 = useCollectionRecord();
const collection = useCollection();
const { allowSelectExistingRecord, allowAddnew, allowDisassociation, enableIndexÏColumn } = field.componentProps;
const { allowSelectExistingRecord, allowAddnew, allowDisassociation, enableIndexColumn } = field.componentProps;
useSubTableSpecialCase({ rootField: field, rootSchema: schema });
@@ -263,7 +263,7 @@ export const SubTable: any = observer(
locale={{
emptyText: <span> {field.editable ? t('Please add or select record') : t('No data')}</span>,
}}
enableIndexÏColumn={enableIndexÏColumn !== false}
enableIndexColumn={enableIndexColumn !== false}
footer={() => (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{field.editable && (
@@ -673,7 +673,7 @@ export const Table: any = withDynamicSchemaProps(
onExpand,
loading,
onClickRow,
enableIndexÏColumn,
enableIndexColumn,
...others
} = { ...others1, ...others2 } as any;
const field = useArrayField(others);
@@ -826,7 +826,7 @@ export const Table: any = withDynamicSchemaProps(
const restProps = useMemo(
() => ({
rowSelection: enableIndexÏColumn
rowSelection: enableIndexColumn
? memoizedRowSelection
? {
type: 'checkbox',
@@ -900,7 +900,7 @@ export const Table: any = withDynamicSchemaProps(
isRowSelect,
memoizedRowSelection,
paginationProps,
enableIndexÏColumn,
enableIndexColumn,
],
);
@@ -59,13 +59,19 @@ ReadPretty.Input = (props: InputReadPrettyProps) => {
return (
<div
className={cls(prefixCls, props.className)}
style={{ overflowWrap: 'break-word', whiteSpace: 'normal', ...props.style }}
style={{
display: 'flex',
alignItems: 'center',
overflowWrap: 'break-word',
whiteSpace: 'normal',
...props.style,
}}
>
{props.addonBefore}
{props.prefix}
{compile(props.addonBefore)}
{compile(props.prefix)}
{props.ellipsis ? <EllipsisWithTooltip ellipsis={props.ellipsis}>{content}</EllipsisWithTooltip> : content}
{props.suffix}
{props.addonAfter}
{compile(props.suffix)}
{compile(props.addonAfter)}
</div>
);
};
@@ -857,7 +857,7 @@ export const Table: any = withDynamicSchemaProps(
const collection = useCollection();
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
const { expandFlag, allIncludesChildren, enableIndexÏColumn } = ctx;
const { expandFlag, allIncludesChildren, enableIndexColumn } = ctx;
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
const paginationProps = usePaginationProps(pagination1, pagination2, props);
const columns = useTableColumns(others, paginationProps);
@@ -1023,7 +1023,7 @@ export const Table: any = withDynamicSchemaProps(
const restProps = useMemo(
() => ({
rowSelection:
enableIndexÏColumn !== false
enableIndexColumn !== false
? memoizedRowSelection
? {
type: 'checkbox',
@@ -1105,7 +1105,7 @@ export const Table: any = withDynamicSchemaProps(
memoizedRowSelection,
paginationProps,
tableBlockContextBasicValue,
enableIndexÏColumn,
enableIndexColumn,
],
);
@@ -182,10 +182,10 @@ export const TableBlockDesigner = () => {
<SchemaSettingsSwitchItem
title={t('Enable index column')}
checked={field.decoratorProps?.enableSelectColumn !== false}
onChange={async (enableIndexÏColumn) => {
onChange={async (enableIndexColumn) => {
field.decoratorProps = field.decoratorProps || {};
field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn;
fieldSchema['x-decorator-props'].enableIndexÏColumn = enableIndexÏColumn;
field.decoratorProps.enableIndexColumn = enableIndexColumn;
fieldSchema['x-decorator-props'].enableIndexColumn = enableIndexColumn;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
@@ -144,7 +144,7 @@ export const DateFilterDynamicComponent = (props) => {
<Select
key="unit"
value={value?.unit}
style={{ maxWidth: 140, minWidth: 130 }}
style={{ maxWidth: 140 }}
onChange={(val) => {
const obj = {
...value,
@@ -158,6 +158,7 @@ export const DateFilterDynamicComponent = (props) => {
{ value: 'month', label: t('Calendar Month') },
{ value: 'year', label: t('Calendar Year') },
]}
popupMatchSelectWidth
/>,
]}
{(value?.type === 'exact' || !value?.type) && <SmartDatePicker {...props} />}
@@ -22,10 +22,14 @@ export default (app: Application) => {
const Collection = app.db.getCollection('collections');
if (Collection) {
// @ts-ignore
await Collection.repository.setApp(app);
// @ts-ignore
await Collection.repository.load();
}
app.log.info('syncing database...');
const force = false;
await app.db.sync({
force,
@@ -155,5 +155,5 @@ test('plugin settings permissions', async ({ page, mockPage, mockRole, updateRol
await expect(page.getByRole('link', { name: 'Users & Permissions' })).toBeVisible();
await expect(page.getByRole('link', { name: 'Authentication' })).toBeVisible();
await page.getByRole('link', { name: 'Users & Permissions' }).click();
await expect(page.getByRole('menuitem', { name: 'Data sources' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Data sources' }).first()).toBeVisible();
});
@@ -0,0 +1 @@
dist
@@ -0,0 +1,2 @@
/node_modules
/src
@@ -0,0 +1,2 @@
export * from './dist/client';
export { default } from './dist/client';
@@ -0,0 +1 @@
module.exports = require('./dist/client/index.js');
@@ -0,0 +1,28 @@
{
"name": "@nocobase/plugin-text-copy",
"version": "1.8.0-alpha.7",
"main": "./dist/server/index.js",
"displayName": "Text copy",
"displayName.zh-CN": "文本复制",
"description": "Add copy button to text fields",
"description.zh-CN": "为文本字段添加复制按钮",
"license": "AGPL-3.0",
"homepage": "https://www.nocobase.com",
"devDependencies": {
"@ant-design/icons": "5.x",
"@formily/react": "2.x",
"antd": "5.x",
"react": "^18.2.0",
"react-i18next": "^11.15.1"
},
"peerDependencies": {
"@nocobase/client": "1.x",
"@nocobase/server": "1.x",
"@nocobase/test": "1.x"
},
"keywords": [
"Clipboard",
"Copy"
],
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
}
@@ -0,0 +1,2 @@
export * from './dist/server';
export { default } from './dist/server';
@@ -0,0 +1 @@
module.exports = require('./dist/server/index.js');
@@ -0,0 +1,112 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Field } from '@formily/core';
import { observer, useField, useFieldSchema } from '@formily/react';
import { Plugin, useColumnSchema, useDesignable, useToken } from '@nocobase/client';
import { Typography } from 'antd';
import _ from 'lodash';
import React, { FC, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
// Define namespace for i18n
const NAMESPACE = 'text-copy';
const TextCopyButton: FC = observer(
() => {
const field = useField<Field>();
const { token } = useToken();
const buttonRef = useRef<HTMLDivElement | null>(null);
const [show, setShow] = React.useState(false);
useEffect(() => {
if (field.value && field.readPretty && buttonRef.current) {
const currentRef = buttonRef.current;
const handleMouseOver = (e: MouseEvent) => {
setShow(true);
};
const handleMouseOut = (e: MouseEvent) => {
setShow(false);
};
currentRef?.parentElement?.addEventListener('mouseover', handleMouseOver);
currentRef?.parentElement?.addEventListener('mouseout', handleMouseOut);
return () => {
currentRef?.parentElement?.removeEventListener('mouseover', handleMouseOver);
currentRef?.parentElement?.removeEventListener('mouseout', handleMouseOut);
};
}
}, [field.readPretty, field.value]);
const hidden = field.readPretty && (!field.value || !show);
return (
<Typography.Text
ref={buttonRef}
copyable={{
text: field.value,
}}
style={{ marginLeft: field.readPretty ? token.marginXXS : 0, opacity: hidden ? 0 : 1 }}
/>
);
},
{
displayName: 'TextCopyButton',
},
);
class PluginTextCopy extends Plugin {
async load() {
const copyButton = <TextCopyButton />;
this.app.addScopes({
TextCopyButton: copyButton,
});
// Add the schema settings to enable/disable the copy functionality
this.app.schemaSettingsManager.addItem('fieldSettings:component:Input', 'enableCopier', {
type: 'switch',
useComponentProps() {
const { t } = useTranslation(NAMESPACE);
const { fieldSchema: tableFieldSchema } = useColumnSchema();
const fieldSchema = useFieldSchema();
const field = useField();
const { dn } = useDesignable();
const schema = tableFieldSchema || fieldSchema;
return {
title: t('Display copy button'),
checked: !!schema['x-component-props']?.addonAfter,
onChange: async (checked) => {
if (checked) {
field.componentProps.addonAfter = copyButton;
_.set(schema, 'x-component-props.addonAfter', '{{TextCopyButton}}');
} else {
field.componentProps.addonAfter = null;
_.unset(schema, 'x-component-props.addonAfter');
}
await dn.emit('patch', {
schema: {
'x-uid': schema['x-uid'],
'x-component-props': {
...schema['x-component-props'],
},
},
});
},
};
},
});
}
}
export default PluginTextCopy;
@@ -0,0 +1,11 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export * from './server';
export { default } from './server';
@@ -0,0 +1,3 @@
{
"Display copy button": "Display copy button"
}
@@ -0,0 +1,3 @@
{
"Display copy button": "コピーボタンを表示"
}
@@ -0,0 +1,3 @@
{
"Display copy button": "显示复制按钮"
}
@@ -0,0 +1,18 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Plugin } from '@nocobase/server';
export class PluginTextCopyServer extends Plugin {
async load() {
// Server-side initialization if needed
}
}
export default PluginTextCopyServer;
@@ -62,7 +62,7 @@ export default class extends Instruction {
}
// NOTE: save job for condition calculation
const job = await processor.saveJob({
const job = processor.saveJob({
status: JOB_STATUS.PENDING,
result: { looped },
nodeId: node.id,
@@ -74,12 +74,17 @@ export default class extends Instruction {
const { checkpoint, calculation, expression, continueOnFalse } = node.config.condition ?? {};
if ((calculation || expression) && !checkpoint) {
const condition = calculateCondition(node, processor);
if (!condition && !continueOnFalse) {
job.set({
status: JOB_STATUS.RESOLVED,
result: { looped, broken: true },
});
return job;
if (!condition) {
if (continueOnFalse) {
job.set('result', { looped: 1 });
processor.saveJob(job);
} else {
job.set({
status: JOB_STATUS.RESOLVED,
result: { looped, broken: true },
});
return job;
}
}
}
}
@@ -114,7 +119,7 @@ export default class extends Instruction {
(branchJob.status < JOB_STATUS.PENDING && loop.config.exit === EXIT.CONTINUE)
) {
job.set({ result: { looped: nextIndex } });
await processor.saveJob(job);
processor.saveJob(job);
const length = getTargetLength(target);
if (nextIndex < length) {
@@ -132,7 +137,7 @@ export default class extends Instruction {
}
}
await processor.run(branch, job);
return processor.exit();
return null;
} else {
job.set({
status: JOB_STATUS.RESOLVED,
@@ -35,6 +35,7 @@ describe('workflow > instructions > loop', () => {
workflow = await WorkflowModel.create({
enabled: true,
sync: true,
type: 'collection',
config: {
mode: 1,
@@ -60,8 +61,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -93,8 +92,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -127,8 +124,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -160,8 +155,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -193,8 +186,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -226,8 +217,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -259,8 +248,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -293,8 +280,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -327,8 +312,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -370,8 +353,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -415,8 +396,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -451,8 +430,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -497,8 +474,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -544,8 +519,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -586,8 +559,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -629,8 +600,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -639,6 +608,84 @@ describe('workflow > instructions > loop', () => {
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
expect(jobs[0].result).toEqual({ looped: 1, broken: true });
});
it('continueOnFalse as true', async () => {
const n1 = await workflow.createNode({
type: 'loop',
config: {
target: 2,
condition: {
continueOnFalse: true,
calculation: {
group: {
type: 'and',
calculations: [
{
calculator: 'equal',
operands: [1, 0],
},
],
},
},
},
},
});
const n2 = await workflow.createNode({
type: 'echo',
upstreamId: n1.id,
branchIndex: 0,
});
const post = await PostRepo.create({ values: { title: 't1' } });
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
expect(jobs.length).toBe(2);
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
expect(jobs[0].result).toEqual({ looped: 2 });
});
it('continueOnFalse as true, second false', async () => {
const n1 = await workflow.createNode({
type: 'loop',
});
await n1.update({
config: {
target: 2,
condition: {
continueOnFalse: true,
calculation: {
group: {
type: 'and',
calculations: [
{
calculator: 'equal',
operands: [1, '{{$scopes.' + n1.key + '.item}}'],
},
],
},
},
},
},
});
const n2 = await workflow.createNode({
type: 'echo',
upstreamId: n1.id,
branchIndex: 0,
});
const post = await PostRepo.create({ values: { title: 't1' } });
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
expect(jobs.length).toBe(2);
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
expect(jobs[0].result).toEqual({ looped: 2 });
});
});
describe('exit', () => {
@@ -665,8 +712,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -700,8 +745,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -735,8 +778,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -770,8 +811,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -782,6 +821,9 @@ describe('workflow > instructions > loop', () => {
});
it('exit as continue with async node inside', async () => {
await workflow.update({
sync: false,
});
const n1 = await workflow.createNode({
type: 'loop',
config: {
@@ -863,8 +905,6 @@ describe('workflow > instructions > loop', () => {
},
});
await sleep(500);
const [execution] = await workflow.getExecutions();
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
@@ -967,8 +1007,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
@@ -1005,8 +1043,6 @@ describe('workflow > instructions > loop', () => {
const post = await PostRepo.create({ values: { title: 't1' } });
await sleep(500);
const [e1] = await workflow.getExecutions();
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
+2
View File
@@ -45,6 +45,7 @@
"@nocobase/plugin-file-manager": "1.8.0-alpha.7",
"@nocobase/plugin-gantt": "1.8.0-alpha.7",
"@nocobase/plugin-graph-collection-manager": "1.8.0-alpha.7",
"@nocobase/plugin-text-copy": "1.8.0-alpha.7",
"@nocobase/plugin-kanban": "1.8.0-alpha.7",
"@nocobase/plugin-locale-tester": "1.8.0-alpha.7",
"@nocobase/plugin-localization": "1.8.0-alpha.7",
@@ -117,6 +118,7 @@
"@nocobase/plugin-field-sort",
"@nocobase/plugin-file-manager",
"@nocobase/plugin-gantt",
"@nocobase/plugin-text-copy",
"@nocobase/plugin-kanban",
"@nocobase/plugin-logger",
"@nocobase/plugin-notification-manager",