@nocobase/client-v2 2.3.0-alpha.1 → 2.3.0-beta.2

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.
Files changed (148) hide show
  1. package/es/APIClient.d.ts +12 -0
  2. package/es/components/form/ScanInput/useCodeScanner.d.ts +2 -2
  3. package/es/flow/actions/linkageRules.d.ts +25 -0
  4. package/es/flow/components/FieldAssignValueInput.d.ts +4 -30
  5. package/es/flow/components/FlowRoute.d.ts +2 -2
  6. package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
  7. package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
  8. package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
  9. package/es/flow/components/field-value-variable/index.d.ts +11 -0
  10. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
  11. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  12. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
  13. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
  14. package/es/flow/models/base/ActionModelCore.d.ts +4 -2
  15. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  16. package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
  17. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
  18. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  19. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
  20. package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
  21. package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
  22. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
  23. package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
  24. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  25. package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
  26. package/es/flow/models/blocks/table/utils.d.ts +1 -0
  27. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
  28. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
  29. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
  30. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  31. package/es/flow-compat/passwordUtils.d.ts +1 -1
  32. package/es/index.d.ts +1 -0
  33. package/es/index.mjs +147 -144
  34. package/es/ui-operation/index.d.ts +15 -0
  35. package/es/ui-operation/ui-operation-codec.d.ts +10 -0
  36. package/lib/index.js +170 -167
  37. package/lib/locale/languageCodes.js +1 -0
  38. package/package.json +8 -7
  39. package/src/APIClient.ts +92 -0
  40. package/src/Application.tsx +3 -1
  41. package/src/__tests__/APIClient.test.tsx +58 -0
  42. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +7 -2
  43. package/src/__tests__/settings-center.test.tsx +270 -6
  44. package/src/acl/ACLProvider.tsx +2 -0
  45. package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
  46. package/src/components/form/ScanInput/CodeScanner.tsx +61 -34
  47. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  48. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +114 -18
  49. package/src/components/form/ScanInput/useCodeScanner.ts +166 -23
  50. package/src/components/form/filter/CollectionFilterItem.tsx +4 -2
  51. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +17 -0
  52. package/src/components/form/table/Table.tsx +81 -6
  53. package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
  54. package/src/flow/__tests__/FlowRoute.test.tsx +126 -8
  55. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  56. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  57. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  58. package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
  59. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
  60. package/src/flow/actions/linkageRules.tsx +59 -16
  61. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
  62. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
  63. package/src/flow/common/Markdown/style.ts +4 -0
  64. package/src/flow/components/BlockItemCard.tsx +2 -2
  65. package/src/flow/components/FieldAssignValueInput.tsx +38 -621
  66. package/src/flow/components/FlowRoute.tsx +71 -29
  67. package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
  68. package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
  69. package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
  70. package/src/flow/components/field-value-variable/dateValue.ts +223 -0
  71. package/src/flow/components/field-value-variable/index.ts +12 -0
  72. package/src/flow/components/filter/VariableFilterItem.tsx +12 -3
  73. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +125 -0
  74. package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
  75. package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
  76. package/src/flow/index.ts +2 -0
  77. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  78. package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
  79. package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
  80. package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
  81. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
  82. package/src/flow/models/base/ActionModelCore.tsx +11 -2
  83. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  84. package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
  85. package/src/flow/models/base/PageModel/PageTabModel.tsx +67 -17
  86. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +509 -12
  87. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  88. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  89. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  90. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
  91. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
  92. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
  93. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
  94. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +27 -3
  95. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
  96. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +129 -14
  97. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
  98. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
  99. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +359 -1
  100. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
  101. package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
  102. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +32 -4
  103. package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
  104. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
  105. package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
  106. package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
  107. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
  108. package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
  109. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
  110. package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
  111. package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
  112. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  113. package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
  114. package/src/flow/models/blocks/table/TableBlockModel.tsx +28 -2
  115. package/src/flow/models/blocks/table/TableColumnModel.tsx +48 -9
  116. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
  117. package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
  118. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  119. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
  120. package/src/flow/models/blocks/table/utils.ts +7 -0
  121. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
  122. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +8 -0
  123. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
  124. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
  125. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
  126. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +9 -136
  127. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
  128. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
  129. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  130. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  131. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
  132. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  133. package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
  134. package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
  135. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  136. package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
  137. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +6 -0
  138. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +27 -2
  139. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
  140. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  141. package/src/index.ts +1 -0
  142. package/src/layout-manager/LayoutContentRoute.tsx +2 -1
  143. package/src/layout-manager/LayoutRoute.tsx +2 -1
  144. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
  145. package/src/locale/languageCodes.ts +1 -0
  146. package/src/settings-center/AdminSettingsLayout.tsx +23 -2
  147. package/src/ui-operation/index.ts +33 -0
  148. package/src/ui-operation/ui-operation-codec.ts +54 -0
@@ -615,6 +615,46 @@ describe('FilterManager', () => {
615
615
  expect(mockTargetModel2.resource.refresh).toHaveBeenCalledTimes(1);
616
616
  });
617
617
 
618
+ it('should reset target interaction state after refreshing filtered data', async () => {
619
+ (filterManager as any).filterConfigs = [
620
+ {
621
+ filterId: 'filter-1',
622
+ targetId: 'target-1',
623
+ filterPaths: ['name'],
624
+ operator: '$eq',
625
+ },
626
+ ];
627
+
628
+ const refresh = vi.fn().mockResolvedValue(undefined);
629
+ const resetAfterFilterChange = vi.fn(() => {
630
+ expect(refresh).toHaveBeenCalledTimes(1);
631
+ });
632
+ const mockTargetModel = {
633
+ resource: {
634
+ addFilterGroup: vi.fn(),
635
+ removeFilterGroup: vi.fn(),
636
+ refresh,
637
+ },
638
+ setFilterActive: vi.fn(),
639
+ getDataLoadingMode: vi.fn().mockReturnValue('auto'),
640
+ resetAfterFilterChange,
641
+ };
642
+ const mockFilterModel = {
643
+ getFilterValue: vi.fn().mockReturnValue('test-value'),
644
+ };
645
+
646
+ (mockFlowModel.flowEngine.getModel as any).mockImplementation((uid: string) => {
647
+ if (uid === 'target-1') return mockTargetModel;
648
+ if (uid === 'filter-1') return mockFilterModel;
649
+ return null;
650
+ });
651
+
652
+ await filterManager.refreshTargetsByFilter('filter-1');
653
+
654
+ expect(resetAfterFilterChange).toHaveBeenCalledTimes(1);
655
+ expect(refresh).toHaveBeenCalledTimes(1);
656
+ });
657
+
618
658
  it('should wait for target resource idle before refreshing', async () => {
619
659
  vi.useFakeTimers();
620
660
  (filterManager as any).filterConfigs = [
@@ -72,17 +72,64 @@ const flowKeepMobileHorizontalClassName = css`
72
72
  function isGridDelegatedStep(flowKey: string, stepKey: string): boolean {
73
73
  return !!GRID_DELEGATED_STEP_KEYS[flowKey]?.has(stepKey);
74
74
  }
75
+
76
+ interface FormModelWithAvailableData {
77
+ hasAvailableData(): boolean;
78
+ }
79
+
80
+ interface FormModelWithSubmit {
81
+ submit(): Promise<unknown>;
82
+ }
83
+
84
+ function hasAvailableDataCapability(model: object): model is FormModelWithAvailableData {
85
+ return 'hasAvailableData' in model && typeof model.hasAvailableData === 'function';
86
+ }
87
+
88
+ function hasSubmitCapability(model: object): model is FormModelWithSubmit {
89
+ return 'submit' in model && typeof model.submit === 'function';
90
+ }
91
+
75
92
  export class FormBlockModel<
76
93
  T extends DefaultCollectionBlockModelStructure = DefaultCollectionBlockModelStructure,
77
94
  > extends CollectionBlockModel<T> {
78
95
  formValueRuntime?: FormValueRuntime;
79
96
 
97
+ serialize() {
98
+ return { ...super.serialize(), variableContractType: { type: 'form', use: this.use } };
99
+ }
100
+
80
101
  private userModifiedTopLevelFields = new Set<string>();
81
102
 
82
103
  get form() {
83
104
  return this.context.form as FormInstance;
84
105
  }
85
106
 
107
+ submitFromRunJs() {
108
+ if (hasAvailableDataCapability(this) && !this.hasAvailableData()) {
109
+ return;
110
+ }
111
+
112
+ const submitAction = this.mapSubModels('actions', (action) => action).find((action) =>
113
+ action.getFlow('submitSettings'),
114
+ );
115
+ if (submitAction) {
116
+ return submitAction.onClick(undefined);
117
+ }
118
+
119
+ const isCoreForm = ['CreateFormModel', 'EditFormModel'].some((modelName) => {
120
+ const ModelClass = this.flowEngine.getModelClass(modelName);
121
+ return ModelClass && this instanceof ModelClass;
122
+ });
123
+ if (this.context.publicFormRuntime || !isCoreForm || !hasSubmitCapability(this)) {
124
+ return;
125
+ }
126
+
127
+ this.submit().catch((error: unknown) => {
128
+ this.context.message.error(this.context.t('Save failed'));
129
+ console.error('Form submission error:', error);
130
+ });
131
+ }
132
+
86
133
  _defaultCustomModelClasses = {
87
134
  FormActionGroupModel: 'FormActionGroupModel',
88
135
  FormItemModel: 'FormItemModel',
@@ -21,6 +21,10 @@ export type DefaultFormGridStructure = {
21
21
  };
22
22
 
23
23
  export class FormGridModel<T extends DefaultFormGridStructure = DefaultFormGridStructure> extends GridModel<T> {
24
+ serialize() {
25
+ return { ...super.serialize(), variableContractType: { type: 'formGrid', use: this.use } };
26
+ }
27
+
24
28
  itemFallback = (<Skeleton.Input block size="small" style={{ marginBottom: '0.5rem' }} />);
25
29
  itemSettingsMenuLevel = 2;
26
30
  itemFlowSettings = {
@@ -465,11 +465,17 @@ describe('FormBlockModel (form/formValues injection & server resolve anchors)',
465
465
 
466
466
  it('builds non-empty contextParams for ctx.formValues.* deep association path', async () => {
467
467
  const model = await setupFormModel();
468
+ const sessionPayload = Buffer.from(JSON.stringify({ userId: 1, signInTime: 'form-record-slots' })).toString(
469
+ 'base64url',
470
+ );
468
471
  // 注入 api mock 到引擎上下文,拦截 variables:resolve 的请求
469
472
  const api = {
473
+ auth: { token: `test.${sessionPayload}.sig` },
470
474
  request: vi.fn(async (config: any) => {
471
- const payload = config?.data?.values || {};
472
- const batch = payload.batch || [];
475
+ const requestValues = config?.data?.values || {};
476
+ const batch = requestValues.batch || [];
477
+ expect(batch[0]?.rd).toEqual(expect.any(String));
478
+ expect(batch[0]?.template).toEqual({ who: '{{ ctx.formValues.assignees.org.name }}' });
473
479
  const cp = batch[0]?.contextParams || {};
474
480
  const keys = Object.keys(cp).sort();
475
481
  // 聚合为单键,不再使用索引键
@@ -0,0 +1,131 @@
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 { FlowEngine } from '@nocobase/flow-engine';
11
+ import { describe, expect, it, vi } from 'vitest';
12
+ import { CreateFormModel } from '../CreateFormModel';
13
+ import { EditFormModel } from '../EditFormModel';
14
+ import { FormActionModel } from '../FormActionModel';
15
+
16
+ function prepareFormModel<T extends CreateFormModel | EditFormModel>(model: T, publicFormRuntime = false) {
17
+ const engine = new FlowEngine();
18
+ engine.registerModels({ CreateFormModel, EditFormModel });
19
+ model.flowEngine = engine;
20
+ Object.defineProperty(model, 'context', {
21
+ value: {
22
+ publicFormRuntime,
23
+ message: { error: vi.fn() },
24
+ t: (value: string) => value,
25
+ },
26
+ });
27
+ return model;
28
+ }
29
+
30
+ function createSubmitAction() {
31
+ const onClick = vi.fn();
32
+ const action = {
33
+ getFlow: vi.fn((key: string) => (key === 'submitSettings' ? {} : undefined)),
34
+ onClick,
35
+ } as unknown as FormActionModel;
36
+ return { action, onClick };
37
+ }
38
+
39
+ describe('FormBlockModel.submitFromRunJs', () => {
40
+ it('dispatches the first action with a submitSettings flow', () => {
41
+ const { action, onClick } = createSubmitAction();
42
+ const { action: secondAction, onClick: secondOnClick } = createSubmitAction();
43
+ const blockModel = prepareFormModel(
44
+ Object.assign(Object.create(CreateFormModel.prototype), {
45
+ mapSubModels: (_key: string, callback: (item: FormActionModel) => FormActionModel) => [
46
+ callback(action),
47
+ callback(secondAction),
48
+ ],
49
+ }) as CreateFormModel,
50
+ );
51
+
52
+ blockModel.submitFromRunJs();
53
+
54
+ expect(onClick).toHaveBeenCalledWith(undefined);
55
+ expect(secondOnClick).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it('does not dispatch an edit submit action without an available record', () => {
59
+ const { action, onClick } = createSubmitAction();
60
+ const blockModel = prepareFormModel(
61
+ Object.assign(Object.create(EditFormModel.prototype), {
62
+ hasAvailableData: () => false,
63
+ mapSubModels: (_key: string, callback: (item: FormActionModel) => FormActionModel) => [callback(action)],
64
+ }) as EditFormModel,
65
+ );
66
+
67
+ blockModel.submitFromRunJs();
68
+
69
+ expect(onClick).not.toHaveBeenCalled();
70
+ });
71
+
72
+ it('falls back to the core form submit handler when no submit action exists', () => {
73
+ const submit = vi.fn().mockResolvedValue(undefined);
74
+ const blockModel = prepareFormModel(
75
+ Object.assign(Object.create(CreateFormModel.prototype), {
76
+ mapSubModels: vi.fn(() => []),
77
+ submit,
78
+ }) as CreateFormModel,
79
+ );
80
+
81
+ blockModel.submitFromRunJs();
82
+
83
+ expect(submit).toHaveBeenCalledOnce();
84
+ });
85
+
86
+ it('falls back to the core edit submit handler when a record exists', () => {
87
+ const submit = vi.fn().mockResolvedValue(undefined);
88
+ const blockModel = prepareFormModel(
89
+ Object.assign(Object.create(EditFormModel.prototype), {
90
+ hasAvailableData: () => true,
91
+ mapSubModels: vi.fn(() => []),
92
+ submit,
93
+ }) as EditFormModel,
94
+ );
95
+
96
+ blockModel.submitFromRunJs();
97
+
98
+ expect(submit).toHaveBeenCalledOnce();
99
+ });
100
+
101
+ it('uses the core fallback for specialized form subclasses', () => {
102
+ class SpecializedCreateFormModel extends CreateFormModel {}
103
+
104
+ const submit = vi.fn().mockResolvedValue(undefined);
105
+ const blockModel = prepareFormModel(
106
+ Object.assign(Object.create(SpecializedCreateFormModel.prototype), {
107
+ mapSubModels: vi.fn(() => []),
108
+ submit,
109
+ }) as SpecializedCreateFormModel,
110
+ );
111
+
112
+ blockModel.submitFromRunJs();
113
+
114
+ expect(submit).toHaveBeenCalledOnce();
115
+ });
116
+
117
+ it('does not use the core fallback for public forms without a submit action', () => {
118
+ const submit = vi.fn().mockResolvedValue(undefined);
119
+ const blockModel = prepareFormModel(
120
+ Object.assign(Object.create(CreateFormModel.prototype), {
121
+ mapSubModels: vi.fn(() => []),
122
+ submit,
123
+ }) as CreateFormModel,
124
+ true,
125
+ );
126
+
127
+ blockModel.submitFromRunJs();
128
+
129
+ expect(submit).not.toHaveBeenCalled();
130
+ });
131
+ });