@nocobase/flow-engine 2.0.0-beta.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/FlowDefinition.d.ts +2 -0
- package/lib/JSRunner.d.ts +6 -0
- package/lib/JSRunner.js +32 -2
- package/lib/ViewScopedFlowEngine.js +3 -0
- package/lib/acl/Acl.js +13 -3
- package/lib/components/FlowContextSelector.js +155 -10
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +5 -1
- package/lib/components/variables/VariableInput.js +9 -4
- package/lib/components/variables/VariableTag.js +46 -39
- package/lib/components/variables/utils.d.ts +7 -0
- package/lib/components/variables/utils.js +42 -2
- package/lib/data-source/index.d.ts +7 -27
- package/lib/data-source/index.js +81 -51
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +163 -22
- package/lib/flowContext.d.ts +230 -7
- package/lib/flowContext.js +2267 -148
- package/lib/flowEngine.d.ts +21 -0
- package/lib/flowEngine.js +56 -8
- package/lib/flowI18n.js +6 -4
- package/lib/flowSettings.js +17 -11
- package/lib/index.d.ts +7 -1
- package/lib/index.js +21 -0
- package/lib/locale/en-US.json +9 -2
- package/lib/locale/index.d.ts +14 -0
- package/lib/locale/zh-CN.json +8 -1
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.js +12 -1
- package/lib/provider.js +5 -5
- package/lib/resources/baseRecordResource.d.ts +5 -0
- package/lib/resources/baseRecordResource.js +24 -0
- package/lib/resources/multiRecordResource.d.ts +1 -0
- package/lib/resources/multiRecordResource.js +11 -4
- package/lib/resources/singleRecordResource.js +2 -0
- package/lib/resources/sqlResource.d.ts +4 -3
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/base.js +706 -41
- package/lib/runjs-context/contributions.d.ts +33 -0
- package/lib/runjs-context/contributions.js +88 -0
- package/lib/runjs-context/helpers.js +12 -1
- package/lib/runjs-context/setup.js +6 -0
- package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
- package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
- package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
- package/lib/runjs-context/snippets/index.d.ts +11 -1
- package/lib/runjs-context/snippets/index.js +61 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
- package/lib/runjsLibs.d.ts +28 -0
- package/lib/runjsLibs.js +532 -0
- package/lib/scheduler/ModelOperationScheduler.d.ts +2 -0
- package/lib/scheduler/ModelOperationScheduler.js +25 -21
- package/lib/types.d.ts +27 -0
- package/lib/utils/associationObjectVariable.d.ts +2 -2
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/createEphemeralContext.js +2 -2
- package/lib/utils/dateVariable.d.ts +16 -0
- package/lib/utils/dateVariable.js +380 -0
- package/lib/utils/exceptions.d.ts +7 -0
- package/lib/utils/exceptions.js +10 -0
- package/lib/utils/index.d.ts +8 -3
- package/lib/utils/index.js +45 -0
- package/lib/utils/params-resolvers.js +16 -9
- package/lib/utils/resolveModuleUrl.d.ts +58 -0
- package/lib/utils/resolveModuleUrl.js +65 -0
- package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
- package/lib/utils/resolveRunJSObjectValues.js +61 -0
- package/lib/utils/runjsModuleLoader.d.ts +58 -0
- package/lib/utils/runjsModuleLoader.js +422 -0
- package/lib/utils/runjsTemplateCompat.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +743 -0
- package/lib/utils/runjsValue.d.ts +29 -0
- package/lib/utils/runjsValue.js +275 -0
- package/lib/utils/safeGlobals.d.ts +18 -8
- package/lib/utils/safeGlobals.js +164 -17
- package/lib/utils/schema-utils.d.ts +10 -0
- package/lib/utils/schema-utils.js +61 -0
- package/lib/views/createViewMeta.d.ts +0 -7
- package/lib/views/createViewMeta.js +19 -70
- package/lib/views/index.d.ts +1 -2
- package/lib/views/index.js +4 -3
- package/lib/views/useDialog.js +7 -2
- package/lib/views/useDrawer.js +7 -2
- package/lib/views/usePage.d.ts +4 -0
- package/lib/views/usePage.js +43 -6
- package/lib/views/usePopover.js +4 -1
- package/lib/views/viewEvents.d.ts +17 -0
- package/lib/views/viewEvents.js +90 -0
- package/package.json +4 -4
- package/src/BlockScopedFlowEngine.ts +2 -5
- package/src/JSRunner.ts +44 -2
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/JSRunner.test.ts +64 -0
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowContext.test.ts +693 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
- package/src/__tests__/flowRuntimeContext.test.ts +2 -1
- package/src/__tests__/flowSettings.open.test.tsx +123 -19
- package/src/__tests__/runjsContext.test.ts +10 -7
- package/src/__tests__/runjsContextImplementations.test.ts +34 -3
- package/src/__tests__/runjsContextRuntime.test.ts +3 -3
- package/src/__tests__/runjsContributions.test.ts +89 -0
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsLocales.test.ts +4 -1
- package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
- package/src/__tests__/runjsSnippets.test.ts +40 -3
- package/src/acl/Acl.tsx +3 -3
- package/src/components/FlowContextSelector.tsx +208 -12
- package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
- package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +109 -16
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +13 -2
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
- package/src/components/variables/VariableInput.tsx +12 -4
- package/src/components/variables/VariableTag.tsx +54 -45
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
- package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
- package/src/components/variables/__tests__/utils.test.ts +81 -3
- package/src/components/variables/utils.ts +67 -6
- package/src/data-source/index.ts +85 -110
- package/src/executor/FlowExecutor.ts +200 -23
- package/src/executor/__tests__/flowExecutor.test.ts +66 -0
- package/src/flowContext.ts +2986 -211
- package/src/flowEngine.ts +59 -8
- package/src/flowI18n.ts +7 -5
- package/src/flowSettings.ts +18 -12
- package/src/index.ts +14 -1
- package/src/locale/en-US.json +9 -2
- package/src/locale/zh-CN.json +8 -1
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +554 -0
- package/src/models/__tests__/flowModel.test.ts +20 -4
- package/src/models/flowModel.tsx +13 -1
- package/src/provider.tsx +7 -6
- package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
- package/src/resources/__tests__/sqlResource.test.ts +60 -0
- package/src/resources/baseRecordResource.ts +31 -0
- package/src/resources/multiRecordResource.ts +11 -4
- package/src/resources/singleRecordResource.ts +3 -0
- package/src/resources/sqlResource.ts +11 -6
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/base.ts +715 -44
- package/src/runjs-context/contributions.ts +88 -0
- package/src/runjs-context/helpers.ts +11 -1
- package/src/runjs-context/setup.ts +6 -0
- package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
- package/src/runjs-context/snippets/index.ts +75 -41
- package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +27 -21
- package/src/types.ts +38 -1
- package/src/utils/__tests__/dateVariable.test.ts +101 -0
- package/src/utils/__tests__/params-resolvers.test.ts +40 -0
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
- package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
- package/src/utils/__tests__/runjsValue.test.ts +44 -0
- package/src/utils/__tests__/safeGlobals.test.ts +57 -2
- package/src/utils/__tests__/utils.test.ts +95 -0
- package/src/utils/associationObjectVariable.ts +2 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/createEphemeralContext.ts +5 -4
- package/src/utils/dateVariable.ts +397 -0
- package/src/utils/exceptions.ts +11 -0
- package/src/utils/index.ts +37 -3
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/resolveRunJSObjectValues.ts +46 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/runjsValue.ts +287 -0
- package/src/utils/safeGlobals.ts +188 -17
- package/src/utils/schema-utils.ts +79 -0
- package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +35 -8
- package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
- package/src/views/createViewMeta.ts +22 -75
- package/src/views/index.tsx +1 -2
- package/src/views/useDialog.tsx +8 -1
- package/src/views/useDrawer.tsx +8 -1
- package/src/views/usePage.tsx +51 -5
- package/src/views/usePopover.tsx +4 -1
- package/src/views/viewEvents.ts +55 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test, expect } from 'vitest';
|
|
11
|
+
import { FlowEngine } from '../../flowEngine';
|
|
12
|
+
import { FlowModel } from '../flowModel';
|
|
13
|
+
|
|
14
|
+
describe('dispatchEvent dynamic event flow phase (scheduleModelOperation integration)', () => {
|
|
15
|
+
test('default (phase undefined): instance flows run before static flows', async () => {
|
|
16
|
+
const engine = new FlowEngine();
|
|
17
|
+
class M extends FlowModel {}
|
|
18
|
+
engine.registerModels({ M });
|
|
19
|
+
|
|
20
|
+
const calls: string[] = [];
|
|
21
|
+
|
|
22
|
+
M.registerFlow({
|
|
23
|
+
key: 'S',
|
|
24
|
+
on: { eventName: 'go' },
|
|
25
|
+
steps: {
|
|
26
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const model = engine.createModel({ use: 'M' });
|
|
31
|
+
model.registerFlow('D', {
|
|
32
|
+
on: { eventName: 'go' },
|
|
33
|
+
steps: {
|
|
34
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await model.dispatchEvent('go');
|
|
39
|
+
expect(calls).toEqual(['dynamic', 'static-a']);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('default (phase undefined): ctx.exitAll() stops static flows (beforeAllFlows regression)', async () => {
|
|
43
|
+
const engine = new FlowEngine();
|
|
44
|
+
class M extends FlowModel {}
|
|
45
|
+
engine.registerModels({ M });
|
|
46
|
+
|
|
47
|
+
const calls: string[] = [];
|
|
48
|
+
|
|
49
|
+
M.registerFlow({
|
|
50
|
+
key: 'S',
|
|
51
|
+
on: { eventName: 'go' },
|
|
52
|
+
steps: {
|
|
53
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
M.registerFlow({
|
|
57
|
+
key: 'T',
|
|
58
|
+
on: { eventName: 'go' },
|
|
59
|
+
steps: {
|
|
60
|
+
t: { handler: async () => void calls.push('static-t') } as any,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const model = engine.createModel({ use: 'M' });
|
|
65
|
+
model.registerFlow('D', {
|
|
66
|
+
on: { eventName: 'go' },
|
|
67
|
+
steps: {
|
|
68
|
+
d: {
|
|
69
|
+
handler: async (ctx: any) => {
|
|
70
|
+
calls.push('dynamic');
|
|
71
|
+
ctx.exitAll();
|
|
72
|
+
},
|
|
73
|
+
} as any,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await model.dispatchEvent('go');
|
|
78
|
+
expect(calls).toEqual(['dynamic']);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("phase='afterAllFlows': instance flow runs after static flows", async () => {
|
|
82
|
+
const engine = new FlowEngine();
|
|
83
|
+
class M extends FlowModel {}
|
|
84
|
+
engine.registerModels({ M });
|
|
85
|
+
|
|
86
|
+
const calls: string[] = [];
|
|
87
|
+
|
|
88
|
+
M.registerFlow({
|
|
89
|
+
key: 'S',
|
|
90
|
+
on: { eventName: 'go' },
|
|
91
|
+
steps: {
|
|
92
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const model = engine.createModel({ use: 'M' });
|
|
97
|
+
model.registerFlow('D', {
|
|
98
|
+
on: { eventName: 'go', phase: 'afterAllFlows' },
|
|
99
|
+
steps: {
|
|
100
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await model.dispatchEvent('go');
|
|
105
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("phase='beforeFlow': instance flow runs before the target static flow", async () => {
|
|
109
|
+
const engine = new FlowEngine();
|
|
110
|
+
class M extends FlowModel {}
|
|
111
|
+
engine.registerModels({ M });
|
|
112
|
+
|
|
113
|
+
const calls: string[] = [];
|
|
114
|
+
|
|
115
|
+
M.registerFlow({
|
|
116
|
+
key: 'S',
|
|
117
|
+
on: { eventName: 'go' },
|
|
118
|
+
steps: {
|
|
119
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
120
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const model = engine.createModel({ use: 'M' });
|
|
125
|
+
model.registerFlow('D', {
|
|
126
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'S' },
|
|
127
|
+
steps: {
|
|
128
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await model.dispatchEvent('go');
|
|
133
|
+
expect(calls).toEqual(['dynamic', 'static-a', 'static-b']);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("phase='afterFlow': instance flow runs after the target static flow", async () => {
|
|
137
|
+
const engine = new FlowEngine();
|
|
138
|
+
class M extends FlowModel {}
|
|
139
|
+
engine.registerModels({ M });
|
|
140
|
+
|
|
141
|
+
const calls: string[] = [];
|
|
142
|
+
|
|
143
|
+
M.registerFlow({
|
|
144
|
+
key: 'S',
|
|
145
|
+
on: { eventName: 'go' },
|
|
146
|
+
steps: {
|
|
147
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
148
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const model = engine.createModel({ use: 'M' });
|
|
153
|
+
model.registerFlow('D', {
|
|
154
|
+
on: { eventName: 'go', phase: 'afterFlow', flowKey: 'S' },
|
|
155
|
+
steps: {
|
|
156
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await model.dispatchEvent('go');
|
|
161
|
+
expect(calls).toEqual(['static-a', 'static-b', 'dynamic']);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("phase='beforeStep': instance flow runs before the target static step", async () => {
|
|
165
|
+
const engine = new FlowEngine();
|
|
166
|
+
class M extends FlowModel {}
|
|
167
|
+
engine.registerModels({ M });
|
|
168
|
+
|
|
169
|
+
const calls: string[] = [];
|
|
170
|
+
|
|
171
|
+
M.registerFlow({
|
|
172
|
+
key: 'S',
|
|
173
|
+
on: { eventName: 'go' },
|
|
174
|
+
steps: {
|
|
175
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
176
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const model = engine.createModel({ use: 'M' });
|
|
181
|
+
model.registerFlow('D', {
|
|
182
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
183
|
+
steps: {
|
|
184
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await model.dispatchEvent('go');
|
|
189
|
+
expect(calls).toEqual(['dynamic', 'static-a', 'static-b']);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("phase='afterStep': instance flow runs after the target static step", async () => {
|
|
193
|
+
const engine = new FlowEngine();
|
|
194
|
+
class M extends FlowModel {}
|
|
195
|
+
engine.registerModels({ M });
|
|
196
|
+
|
|
197
|
+
const calls: string[] = [];
|
|
198
|
+
|
|
199
|
+
M.registerFlow({
|
|
200
|
+
key: 'S',
|
|
201
|
+
on: { eventName: 'go' },
|
|
202
|
+
steps: {
|
|
203
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
204
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const model = engine.createModel({ use: 'M' });
|
|
209
|
+
model.registerFlow('D', {
|
|
210
|
+
on: { eventName: 'go', phase: 'afterStep', flowKey: 'S', stepKey: 'a' },
|
|
211
|
+
steps: {
|
|
212
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await model.dispatchEvent('go');
|
|
217
|
+
expect(calls).toEqual(['static-a', 'dynamic', 'static-b']);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("phase='beforeFlow': ctx.exitAll() stops anchor flow and subsequent flows", async () => {
|
|
221
|
+
const engine = new FlowEngine();
|
|
222
|
+
class M extends FlowModel {}
|
|
223
|
+
engine.registerModels({ M });
|
|
224
|
+
|
|
225
|
+
const calls: string[] = [];
|
|
226
|
+
|
|
227
|
+
M.registerFlow({
|
|
228
|
+
key: 'S',
|
|
229
|
+
on: { eventName: 'go' },
|
|
230
|
+
steps: {
|
|
231
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
M.registerFlow({
|
|
235
|
+
key: 'T',
|
|
236
|
+
on: { eventName: 'go' },
|
|
237
|
+
steps: {
|
|
238
|
+
t: { handler: async () => void calls.push('static-t') } as any,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const model = engine.createModel({ use: 'M' });
|
|
243
|
+
model.registerFlow('D', {
|
|
244
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'S' },
|
|
245
|
+
steps: {
|
|
246
|
+
d: {
|
|
247
|
+
handler: async (ctx: any) => {
|
|
248
|
+
calls.push('dynamic');
|
|
249
|
+
ctx.exitAll();
|
|
250
|
+
},
|
|
251
|
+
} as any,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
await model.dispatchEvent('go');
|
|
256
|
+
expect(calls).toEqual(['dynamic']);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("phase='beforeStep': ctx.exitAll() stops anchor step and subsequent flows", async () => {
|
|
260
|
+
const engine = new FlowEngine();
|
|
261
|
+
class M extends FlowModel {}
|
|
262
|
+
engine.registerModels({ M });
|
|
263
|
+
|
|
264
|
+
const calls: string[] = [];
|
|
265
|
+
|
|
266
|
+
M.registerFlow({
|
|
267
|
+
key: 'S',
|
|
268
|
+
on: { eventName: 'go' },
|
|
269
|
+
steps: {
|
|
270
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
271
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
M.registerFlow({
|
|
275
|
+
key: 'T',
|
|
276
|
+
on: { eventName: 'go' },
|
|
277
|
+
steps: {
|
|
278
|
+
t: { handler: async () => void calls.push('static-t') } as any,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const model = engine.createModel({ use: 'M' });
|
|
283
|
+
model.registerFlow('D', {
|
|
284
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
285
|
+
steps: {
|
|
286
|
+
d: {
|
|
287
|
+
handler: async (ctx: any) => {
|
|
288
|
+
calls.push('dynamic');
|
|
289
|
+
ctx.exitAll();
|
|
290
|
+
},
|
|
291
|
+
} as any,
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await model.dispatchEvent('go');
|
|
296
|
+
expect(calls).toEqual(['dynamic']);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("phase='afterStep': ctx.exitAll() stops subsequent steps and subsequent flows", async () => {
|
|
300
|
+
const engine = new FlowEngine();
|
|
301
|
+
class M extends FlowModel {}
|
|
302
|
+
engine.registerModels({ M });
|
|
303
|
+
|
|
304
|
+
const calls: string[] = [];
|
|
305
|
+
|
|
306
|
+
M.registerFlow({
|
|
307
|
+
key: 'S',
|
|
308
|
+
on: { eventName: 'go' },
|
|
309
|
+
steps: {
|
|
310
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
311
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
M.registerFlow({
|
|
315
|
+
key: 'T',
|
|
316
|
+
on: { eventName: 'go' },
|
|
317
|
+
steps: {
|
|
318
|
+
t: { handler: async () => void calls.push('static-t') } as any,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const model = engine.createModel({ use: 'M' });
|
|
323
|
+
model.registerFlow('D', {
|
|
324
|
+
on: { eventName: 'go', phase: 'afterStep', flowKey: 'S', stepKey: 'a' },
|
|
325
|
+
steps: {
|
|
326
|
+
d: {
|
|
327
|
+
handler: async (ctx: any) => {
|
|
328
|
+
calls.push('dynamic');
|
|
329
|
+
ctx.exitAll();
|
|
330
|
+
},
|
|
331
|
+
} as any,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await model.dispatchEvent('go');
|
|
336
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("phase='afterFlow': ctx.exitAll() stops subsequent flows", async () => {
|
|
340
|
+
const engine = new FlowEngine();
|
|
341
|
+
class M extends FlowModel {}
|
|
342
|
+
engine.registerModels({ M });
|
|
343
|
+
|
|
344
|
+
const calls: string[] = [];
|
|
345
|
+
|
|
346
|
+
M.registerFlow({
|
|
347
|
+
key: 'S',
|
|
348
|
+
on: { eventName: 'go' },
|
|
349
|
+
steps: {
|
|
350
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
351
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
M.registerFlow({
|
|
355
|
+
key: 'T',
|
|
356
|
+
on: { eventName: 'go' },
|
|
357
|
+
steps: {
|
|
358
|
+
t: { handler: async () => void calls.push('static-t') } as any,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const model = engine.createModel({ use: 'M' });
|
|
363
|
+
model.registerFlow('D', {
|
|
364
|
+
on: { eventName: 'go', phase: 'afterFlow', flowKey: 'S' },
|
|
365
|
+
steps: {
|
|
366
|
+
d: {
|
|
367
|
+
handler: async (ctx: any) => {
|
|
368
|
+
calls.push('dynamic');
|
|
369
|
+
ctx.exitAll();
|
|
370
|
+
},
|
|
371
|
+
} as any,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
await model.dispatchEvent('go');
|
|
376
|
+
expect(calls).toEqual(['static-a', 'static-b', 'dynamic']);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("phase='beforeFlow' missing flow: falls back to afterAllFlows", async () => {
|
|
380
|
+
const engine = new FlowEngine();
|
|
381
|
+
class M extends FlowModel {}
|
|
382
|
+
engine.registerModels({ M });
|
|
383
|
+
|
|
384
|
+
const calls: string[] = [];
|
|
385
|
+
|
|
386
|
+
M.registerFlow({
|
|
387
|
+
key: 'S',
|
|
388
|
+
on: { eventName: 'go' },
|
|
389
|
+
steps: {
|
|
390
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const model = engine.createModel({ use: 'M' });
|
|
395
|
+
model.registerFlow('D', {
|
|
396
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'missing' },
|
|
397
|
+
steps: {
|
|
398
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
await model.dispatchEvent('go');
|
|
403
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("phase='beforeStep' missing step: falls back to afterAllFlows", async () => {
|
|
407
|
+
const engine = new FlowEngine();
|
|
408
|
+
class M extends FlowModel {}
|
|
409
|
+
engine.registerModels({ M });
|
|
410
|
+
|
|
411
|
+
const calls: string[] = [];
|
|
412
|
+
|
|
413
|
+
M.registerFlow({
|
|
414
|
+
key: 'S',
|
|
415
|
+
on: { eventName: 'go' },
|
|
416
|
+
steps: {
|
|
417
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const model = engine.createModel({ use: 'M' });
|
|
422
|
+
model.registerFlow('D', {
|
|
423
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'missing' },
|
|
424
|
+
steps: {
|
|
425
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
await model.dispatchEvent('go');
|
|
430
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('multiple flows on same anchor: executes by flow.sort asc (stable)', async () => {
|
|
434
|
+
const engine = new FlowEngine();
|
|
435
|
+
class M extends FlowModel {}
|
|
436
|
+
engine.registerModels({ M });
|
|
437
|
+
|
|
438
|
+
const calls: string[] = [];
|
|
439
|
+
|
|
440
|
+
M.registerFlow({
|
|
441
|
+
key: 'S',
|
|
442
|
+
on: { eventName: 'go' },
|
|
443
|
+
steps: {
|
|
444
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const model = engine.createModel({ use: 'M' });
|
|
449
|
+
model.registerFlow('D5', {
|
|
450
|
+
sort: 5,
|
|
451
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
452
|
+
steps: {
|
|
453
|
+
d: { handler: async () => void calls.push('dynamic-5') } as any,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
model.registerFlow('D0', {
|
|
457
|
+
sort: 0,
|
|
458
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
459
|
+
steps: {
|
|
460
|
+
d: { handler: async () => void calls.push('dynamic-0') } as any,
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
await model.dispatchEvent('go');
|
|
465
|
+
expect(calls).toEqual(['dynamic-0', 'dynamic-5', 'static-a']);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
describe('dispatchEvent static flow phase (scheduleModelOperation integration)', () => {
|
|
470
|
+
test("phase='beforeFlow': static flow runs before the target static flow", async () => {
|
|
471
|
+
const engine = new FlowEngine();
|
|
472
|
+
class M extends FlowModel {}
|
|
473
|
+
engine.registerModels({ M });
|
|
474
|
+
|
|
475
|
+
const calls: string[] = [];
|
|
476
|
+
|
|
477
|
+
M.registerFlow({
|
|
478
|
+
key: 'S',
|
|
479
|
+
on: { eventName: 'go' },
|
|
480
|
+
steps: {
|
|
481
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
M.registerFlow({
|
|
486
|
+
key: 'P',
|
|
487
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'S' },
|
|
488
|
+
steps: {
|
|
489
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const model = engine.createModel({ use: 'M' });
|
|
494
|
+
await model.dispatchEvent('go');
|
|
495
|
+
expect(calls).toEqual(['phase', 'static-a']);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("phase='afterStep': static flow runs after the target static step", async () => {
|
|
499
|
+
const engine = new FlowEngine();
|
|
500
|
+
class M extends FlowModel {}
|
|
501
|
+
engine.registerModels({ M });
|
|
502
|
+
|
|
503
|
+
const calls: string[] = [];
|
|
504
|
+
|
|
505
|
+
M.registerFlow({
|
|
506
|
+
key: 'S',
|
|
507
|
+
on: { eventName: 'go' },
|
|
508
|
+
steps: {
|
|
509
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
510
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
M.registerFlow({
|
|
515
|
+
key: 'P',
|
|
516
|
+
on: { eventName: 'go', phase: 'afterStep', flowKey: 'S', stepKey: 'a' },
|
|
517
|
+
steps: {
|
|
518
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const model = engine.createModel({ use: 'M' });
|
|
523
|
+
await model.dispatchEvent('go');
|
|
524
|
+
expect(calls).toEqual(['static-a', 'phase', 'static-b']);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("phase='afterAllFlows': static flow runs after static flows", async () => {
|
|
528
|
+
const engine = new FlowEngine();
|
|
529
|
+
class M extends FlowModel {}
|
|
530
|
+
engine.registerModels({ M });
|
|
531
|
+
|
|
532
|
+
const calls: string[] = [];
|
|
533
|
+
|
|
534
|
+
M.registerFlow({
|
|
535
|
+
key: 'S',
|
|
536
|
+
on: { eventName: 'go' },
|
|
537
|
+
steps: {
|
|
538
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
M.registerFlow({
|
|
543
|
+
key: 'P',
|
|
544
|
+
on: { eventName: 'go', phase: 'afterAllFlows' },
|
|
545
|
+
steps: {
|
|
546
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
const model = engine.createModel({ use: 'M' });
|
|
551
|
+
await model.dispatchEvent('go');
|
|
552
|
+
expect(calls).toEqual(['static-a', 'phase']);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
@@ -353,7 +353,7 @@ describe('FlowModel', () => {
|
|
|
353
353
|
}).toThrow('FlowModel must be initialized with a FlowEngine instance.');
|
|
354
354
|
});
|
|
355
355
|
|
|
356
|
-
test('should handle
|
|
356
|
+
test('should handle ctx.exit() as FlowExitAllException in applyFlow', async () => {
|
|
357
357
|
const exitFlow: FlowDefinitionOptions = {
|
|
358
358
|
key: 'exitFlow',
|
|
359
359
|
steps: {
|
|
@@ -374,14 +374,14 @@ describe('FlowModel', () => {
|
|
|
374
374
|
|
|
375
375
|
const result = await model.applyFlow('exitFlow');
|
|
376
376
|
|
|
377
|
-
expect(result).
|
|
377
|
+
expect(result).toBeInstanceOf(FlowExitAllException);
|
|
378
378
|
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
379
379
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
|
|
380
380
|
|
|
381
381
|
consoleSpy.mockRestore();
|
|
382
382
|
});
|
|
383
383
|
|
|
384
|
-
test('should handle
|
|
384
|
+
test('should handle ctx.exit() as FlowExitAllException in beforeRender dispatch', async () => {
|
|
385
385
|
const exitFlow: FlowDefinitionOptions = {
|
|
386
386
|
key: 'exitFlow',
|
|
387
387
|
steps: {
|
|
@@ -413,7 +413,7 @@ describe('FlowModel', () => {
|
|
|
413
413
|
await model.dispatchEvent('beforeRender');
|
|
414
414
|
|
|
415
415
|
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
416
|
-
expect(exitFlow2.steps.step2.handler).toHaveBeenCalled();
|
|
416
|
+
expect(exitFlow2.steps.step2.handler).not.toHaveBeenCalled();
|
|
417
417
|
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowEngine]'));
|
|
418
418
|
|
|
419
419
|
loggerSpy.mockRestore();
|
|
@@ -1558,6 +1558,22 @@ describe('FlowModel', () => {
|
|
|
1558
1558
|
expect(model.forks.size).toBe(1);
|
|
1559
1559
|
});
|
|
1560
1560
|
|
|
1561
|
+
test('should recreate cached fork after dispose to avoid state leakage', () => {
|
|
1562
|
+
const fork1 = model.createFork({ foo: 'bar' }, 'cacheKey');
|
|
1563
|
+
fork1.hidden = true;
|
|
1564
|
+
fork1.setProps({ disabled: true });
|
|
1565
|
+
|
|
1566
|
+
fork1.dispose();
|
|
1567
|
+
|
|
1568
|
+
expect(model.getFork('cacheKey')).toBeUndefined();
|
|
1569
|
+
|
|
1570
|
+
const fork2 = model.createFork({}, 'cacheKey');
|
|
1571
|
+
|
|
1572
|
+
expect(fork2).not.toBe(fork1);
|
|
1573
|
+
expect(fork2.hidden).toBe(false);
|
|
1574
|
+
expect(fork2.localProps).toEqual({});
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1561
1577
|
test('should create different instances for different keys', () => {
|
|
1562
1578
|
const fork1 = model.createFork({}, 'key1');
|
|
1563
1579
|
const fork2 = model.createFork({}, 'key2');
|
package/src/models/flowModel.tsx
CHANGED
|
@@ -424,7 +424,19 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
424
424
|
const meta = Cls.meta as any;
|
|
425
425
|
const metaCreate = meta?.createModelOptions;
|
|
426
426
|
if (metaCreate && typeof metaCreate === 'object' && metaCreate.subModels) {
|
|
427
|
-
|
|
427
|
+
const replaceArrays = (objValue: unknown, srcValue: unknown) => {
|
|
428
|
+
if (Array.isArray(objValue) && Array.isArray(srcValue)) {
|
|
429
|
+
// Arrays should be replaced, not merged by index.
|
|
430
|
+
return srcValue;
|
|
431
|
+
}
|
|
432
|
+
return undefined;
|
|
433
|
+
};
|
|
434
|
+
mergedSubModels = _.mergeWith(
|
|
435
|
+
{},
|
|
436
|
+
_.cloneDeep(metaCreate.subModels || {}),
|
|
437
|
+
_.cloneDeep(subModels || {}),
|
|
438
|
+
replaceArrays,
|
|
439
|
+
);
|
|
428
440
|
}
|
|
429
441
|
} catch (e) {
|
|
430
442
|
// Fallback silently if meta defaults resolution fails
|
package/src/provider.tsx
CHANGED
|
@@ -58,18 +58,19 @@ export const FlowEngineGlobalsContextProvider: React.FC<{ children: React.ReactN
|
|
|
58
58
|
cache: false,
|
|
59
59
|
get: (ctx) => new FlowViewer(ctx, { drawer, embed, popover, dialog }),
|
|
60
60
|
});
|
|
61
|
-
// 将 themeToken 定义为 observable, 使组件能够响应主题的变更
|
|
62
|
-
engine.context.defineProperty('themeToken', {
|
|
63
|
-
get: () => token,
|
|
64
|
-
observable: true,
|
|
65
|
-
cache: true,
|
|
66
|
-
});
|
|
67
61
|
for (const item of Object.entries(context)) {
|
|
68
62
|
const [key, value] = item;
|
|
69
63
|
if (value) {
|
|
70
64
|
engine.context.defineProperty(key, { value });
|
|
71
65
|
}
|
|
72
66
|
}
|
|
67
|
+
// 将 themeToken 定义为 observable, 使组件能够响应主题的变更
|
|
68
|
+
// NOTE: 必须在 antdConfig 写入后再更新 themeToken;否则会读取到旧 antdConfig 的值。
|
|
69
|
+
engine.context.defineProperty('themeToken', {
|
|
70
|
+
get: () => token,
|
|
71
|
+
observable: true,
|
|
72
|
+
cache: true,
|
|
73
|
+
});
|
|
73
74
|
engine.reactView.refresh();
|
|
74
75
|
}, [engine, drawer, modal, message, notification, config, popover, token, dialog, embed]);
|
|
75
76
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
11
|
+
import { FlowEngine } from '../../flowEngine';
|
|
12
|
+
import { MultiRecordResource } from '../multiRecordResource';
|
|
13
|
+
|
|
14
|
+
function createMultiRecordResource() {
|
|
15
|
+
const engine = new FlowEngine();
|
|
16
|
+
return engine.createResource(MultiRecordResource);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('MultiRecordResource - refresh', () => {
|
|
20
|
+
it('should coalesce multiple refresh calls and settle all awaiters', async () => {
|
|
21
|
+
vi.useFakeTimers();
|
|
22
|
+
try {
|
|
23
|
+
const r = createMultiRecordResource();
|
|
24
|
+
const api = {
|
|
25
|
+
request: vi.fn().mockResolvedValue({
|
|
26
|
+
data: { data: [], meta: { count: 0, page: 1, pageSize: 20 } },
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
r.setAPIClient(api as any);
|
|
31
|
+
r.setResourceName('posts');
|
|
32
|
+
|
|
33
|
+
const p1 = r.refresh();
|
|
34
|
+
const p2 = r.refresh();
|
|
35
|
+
|
|
36
|
+
await vi.runAllTimersAsync();
|
|
37
|
+
await expect(p1).resolves.toBeUndefined();
|
|
38
|
+
await expect(p2).resolves.toBeUndefined();
|
|
39
|
+
expect(api.request).toHaveBeenCalledTimes(1);
|
|
40
|
+
} finally {
|
|
41
|
+
vi.useRealTimers();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|